Description
给出n,m,求
∑i=1n∑j=1,j≠im(nmodi)∗(mmodj)
答案mod 19940417
Solution
转化
看到题目,题意十分简洁,心里十分舒畅。
看到
ni
就要想到
ni=n−⌊ni⌋∗i
,然后
⌊ni⌋
是可以用分块做的,下面再讲。
我们上面的式子可以转化为
∑i=1n∑j=1m(n−⌊ni⌋∗i)∗(m−⌊mi⌋∗i)−∑i=1min(n,m)(n−⌊ni⌋∗i)∗(m−⌊mi⌋∗i)
因为我们把所有情况求出来后,还要减去重复的,就是i=j的情况要减去
然后把后面的括号拆开
∑i=1n∑j=1m(n−⌊ni⌋∗i)∗(m−⌊mi⌋∗i)−∑i=1min(n,m)(nm+i2(⌊ni⌋+⌊mi⌋)−i∗(m∗⌊ni⌋+n∗⌊mi⌋))
然后就
如何分块
我们想有一段区间[l,r], ⌊nl⌋=⌊nr⌋ ,因为r要求是满足这个条件最大的值,那么就是说 ⌊nl⌋<=nr ,那么就是 r<=⌊n⌊nl⌋⌋ ,因为r是整数,所以还要取整。那么就是[l,r]之间都是满足这个条件的,那么这段区间的答案就可以根据乘法分配律之类的,用 ⌊nl⌋ 乘上这段和或者是一些东西就可以了。长练分块打法好,危难来时把命保。
还要注意一个东西
∑i=1ni2=n∗(n+1)∗(2∗n+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);
}