天天看點

整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&BZOJ2956)詳解

題目描述

整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&BZOJ2956)詳解

mod 19940417 的值

輸入格式

兩個整數n m

輸出格式

答案 mod 19940417

輸入輸出樣例

輸入 

3 4           

輸出 

1           

輸入

123456 654321           

輸出 #2複制

116430           

說明/提示

30%: n,m <= 1000

60%: n,m <= 10^6

100% n,m <= 10^9

。。。。公式很快就可以推出來。。。就是被傻逼BUG卡了好久QAQ。

我們可以用不考慮i==j的情況扣去i==j的情況,于是等式就變成了:

整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&amp;BZOJ2956)詳解

那麼前面一坨的東西很好求,就是一個比較裸的整數分塊,我們可以直接将拆開:

整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&amp;BZOJ2956)詳解

那麼這一坨就宣告結束了,如果不會的話請看戳這裡

重點是後面的一坨東西。我們可以将它化簡一下:(假設k=min(n,m))

整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&amp;BZOJ2956)詳解
整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&amp;BZOJ2956)詳解
整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&amp;BZOJ2956)詳解

然後我智障了。。。中間兩項的前面多加了一個k,找了半天BUG。。。

那麼前面的三項挺好求的,就不多說了,主要是最後的一項。

對于最後一項我們同樣可以分塊,我們知道

整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&amp;BZOJ2956)詳解

是可以分塊的,那麼同樣的

整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&amp;BZOJ2956)詳解

也可以分塊,後者是前者的整數倍,那麼後者的分塊絕對也是前者的整數倍,也就是說雖然我們無法一次性求得後者的塊的左端點和右端點,但我們可以通過前者的左右端點來一塊塊加上:

ll ans=0;
for (ll l=1,r=0; l<=k; l=r+1){
    r=min(n/(n/l),m/(m/l));
    ll p1=n/l,p2=m/l;
    ans=(ans+((p1*p2%mod)*sum2(l,r)%mod)*inv6%mod)%mod;
}           

這裡還有一個傻逼BUG。。。乘了兩次(r-l+1)。。。sum2裡面已經求過了,我又再求了一次。。。

這裡說明一下,由于p模數不是素數。。。。是以我們不能用費馬定理求逆元。。sum2是i^2的求和公式具體的如下:

整數分塊毒瘤題(非素數模)--模積和(洛谷P2260&amp;BZOJ2956)詳解

。。。至于怎麼推的,哈哈哈哈,我也不知道。(說真的,傻逼BUG真的找得挺自卑的QAQ)

以下是AC代碼:

#include <bits/stdc++.h>
using namespace std;

#define ll long long

const ll mod=19940417;

ll inv2,inv6;

ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if (b==0) {
        x=1;y=0;
        return a;
    }
    ll ret=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return ret;
}

ll getinv(ll a,ll mod)
{
    ll x,y;
    ll d=exgcd(a,mod,x,y);
    return d==1?(x%mod+mod)%mod:-1;
}

ll solve(ll n)
{
    ll ans=0;
    for (ll l=1,r=0; l<=n; l=r+1){
        r=n/(n/l);
        ans=(ans+((((r+l)%mod)*(r-l+1)%mod)*(n/l)%mod)*inv2%mod)%mod;
    }
    return ans;
}

ll work(ll k,ll n)
{
    ll ans=0;
    for (ll l=1,r=0; l<=k; l=r+1){
        r=min(k,n/(n/l));//注意
        ans=(ans+((((r+l)%mod)*(r-l+1)%mod)*(n/l)%mod)*inv2%mod)%mod;
    }
    return ans;
}

ll sum2(ll l,ll r)
{
    l--;
    ll ans1=0,ans2=0;
    ans1=(l*(l+1)%mod)*(2*l+1)%mod;
    ans2=(r*(r+1)%mod)*(2*r+1)%mod;
    return (ans2-ans1+mod)%mod;
}

ll deal(ll k,ll n,ll m)
{
    ll ans=0;
    for (ll l=1,r=0; l<=k; l=r+1){
        r=min(n/(n/l),m/(m/l));//注意
        ll p1=n/l,p2=m/l;
        ans=(ans+((p1*p2%mod)*sum2(l,r)%mod)*inv6%mod)%mod;
    }
    return ans;
}

int main()
{
    ll n,m;
    cin>>n>>m;
    inv2=getinv(2ll,mod);
    inv6=getinv(6ll,mod);
    ll ans=0,ans1=0,ans2=0,ans3=0;
    ans=((n*n%mod-solve(n)+mod)%mod)*((m*m%mod-solve(m)+mod)%mod)%mod;
   // cout<<ans<<endl;
    ll k=min(n,m);
    ans1=(k*n%mod)*m%mod;
    //cout<<(ans-ans1+mod)%mod<<endl;
    //cout<<endl;
    
    ans2=(m*work(k,n)%mod+n*work(k,m)%mod)%mod;
    ans3=deal(k,n,m);
    //cout<<ans2<<endl<<ans3<<endl;
    cout<<(((ans-ans1+mod)%mod+ans2)%mod-ans3+mod)%mod<<endl;
    return 0;
}