【WinterCamp 2013】模积和

Description

给出n,m,求

i=1nj=1,jim(nmodi)(mmodj)

答案mod 19940417

Solution

转化

看到题目,题意十分简洁,心里十分舒畅。
看到 ni 就要想到 ni=nnii ,然后 ni 是可以用分块做的,下面再讲。
我们上面的式子可以转化为

i=1nj=1m(nnii)(mmii)i=1min(n,m)(nnii)(mmii)

因为我们把所有情况求出来后,还要减去重复的,就是i=j的情况要减去
然后把后面的括号拆开
i=1nj=1m(nnii)(mmii)i=1min(n,m)(nm+i2(ni+mi)i(mni+nmi))

然后就

如何分块

我们想有一段区间[l,r], nl=nr ,因为r要求是满足这个条件最大的值,那么就是说 nl<=nr ,那么就是 r<=nnl ,因为r是整数,所以还要取整。那么就是[l,r]之间都是满足这个条件的,那么这段区间的答案就可以根据乘法分配律之类的,用 nl 乘上这段和或者是一些东西就可以了。长练分块打法好,危难来时把命保。

还要注意一个东西

i=1ni2=n(n+1)(2n+1)6

这个东西有很多证明方法,你可以用数学归纳法,百度上有。
看了拉格朗日插值法之后,你会觉得很简单 拉格朗日插值法
其实,等你知道了解决自然数幂和的各种方法之后,你会觉得及其简单 解决自然数幂和的各种方法

除以6的要用逆元

对于mod 19940417的逆元是3323403

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
const int mo=19940417;
const int ni6=3323403;
using namespace std;
ll i,j,k,l,t,n,m,ans,r,k1;
ll sum(ll x,ll y){
    return ((y-x+1)*(y+x)/2)%mo;
}
ll qiuhe(ll x,ll y){
    return ((x+y)*(y-x+1)/2)%mo;
}
ll suan(ll x){
    ll t=0;
    for(ll l=1,r;l<=x;l=r+1){
        r=x/(x/l);
        ll o=x/l;
        t=(t+(r-l+1)*x%mo-qiuhe(l,r)*o%mo+mo)%mo;
    }
    return t;
}
ll pingfanghe(ll x){
    ll t=0;
    t=x*(x+1)%mo*(2*x+1)%mo*ni6%mo;
    return t;
}
int main(){
    scanf("%lld%lld",&n,&m);
    if(n>m)swap(n,m);
    ans=suan(n)*suan(m)%mo;
    for(l=1;l<=n;l=r+1){
        r=min(n/(n/l),m/(m/l));
        ll p=n/l,q=m/l;
        ans=(ans-n*m%mo*(r-l+1)%mo+mo)%mo;
        ans=(ans-(pingfanghe(r)-pingfanghe(l-1)+mo)%mo*p%mo*q%mo)%mo;
        ans=(ans+qiuhe(l,r)*(m*p%mo+n*q%mo)%mo+mo)%mo;
    }
    ans=(ans%mo+mo)%mo;
    printf("%lld\n",ans);
}

你可能感兴趣的:(wc,冬令营,WinterCamp,模积和,分块大法)