做这题之前我先做了一道水题也就是这题的弱化版热了热身。
弱化版水题:P2261 [CQOI2007]余数求和
先讲弱化版的。我们知道一个结论就是k%i=k-k/i*i;
那么我们就把取模拆成这种形式。(下文默认i∈[1,n])
很明显∑(k%i)可以拆成∑k-∑k/i*i,化简一下就是n*k-∑k/i*i。然后我们注意到对于一些i,k/i是一样的,就是数论分块里面块的大小。
再结合高斯速算公式来一波,代码就很好写了。
注意特殊情况就是k/l==0,此时不能直接(k/(k/l)),要把r设为n
大概这样。
1 #include<cstdio> 2 #define it register int 3 #define il inline 4 int n,k; 5 long long ans; 6 il int Min(it p,it q){ 7 return p<q?p:q; 8 } 9 int main(){ 10 scanf("%d%d",&n,&k),ans=1ll*n*k; 11 for(it l=1,r;l<=n;l=r+1) 12 r=(k/l?Min(k/(k/l),n):n),ans=ans-1ll*(r-l+1)*(l+r)/2*1ll*(k/l); 13 printf("%lld",ans); 14 return 0; 15 }
然后我们讲主题:P2260 [清华集训2012]模积和
由于我不会用LaTeX写数学公式,所以转一篇博文:戳这里 讲的非常清楚。
这里稍微提一下。
首先求逆元先分解质因数然后用a-1 mod p = aΦ(p)-1 mod p 去计算
然后就是把 i mod j 转化为 i-(i/j)*j 其中i/j由于c++的性质自动下取整
接着就是容斥原理把i!=j给消掉
然后整理一下式子,把剩下的往里面代。
一个小细节就是默认n<=m 如果读入的时候n>m就交换一下
1 #include<cstdio> 2 #define it register int 3 #define il inline 4 #define rll register long long 5 typedef long long ll; 6 typedef unsigned long long ull; 7 typedef long double ldb; 8 const long long p=19940417; 9 ll inv,n,m,ans; 10 il ll mul(ll a,ll b){ 11 return (a*b-(ull)((ldb)a/p*b+1e-7)*p+p)%p; 12 } 13 il void ksm(ll x,ll l){ 14 inv=1; 15 while(l){ 16 if(l&1) inv=mul(inv,x); 17 l>>=1,x=mul(x,x); 18 } 19 } 20 il ll sumsl(ll l,ll r){ 21 return 1ll*(l+r)*(r-l+1)/2%p; 22 } 23 il ll sumn2(ll x){ 24 return mul(mul(x,x+1),mul(x<<1|1,inv)); 25 } 26 il ll rc(ll x){ 27 ll now=0; 28 for(rll l=1,r;l<=x;l=r+1) 29 r=(x/(x/l)),now=(now+mul(x,r-l+1)-mul(sumsl(l,r),x/l)+p)%p; 30 return now; 31 } 32 il ll mol(ll x){ 33 return x<0?x+p:(x>=p?x-p:x); 34 } 35 il ll Min(ll p,ll q){ 36 return p<q?p:q; 37 } 38 int main(){ 39 scanf("%lld%lld",&n,&m); 40 if(n>m) n+=m,m=n-m,n-=m; 41 ksm(6,17091779);ans=mul(rc(n),rc(m)); 42 for(rll l=1,r,s1,s2,s3;l<=n;l=r+1){ 43 r=Min(n/(n/l),m/(m/l)); 44 s1=mul(mul(n,m),r-l+1); 45 s2=mul(mul(m/l,n/l),sumn2(r)-sumn2(l-1)+p); 46 s3=mul(mol(mul(n/l,m)+mul(m/l,n)),sumsl(l,r)); 47 ans=mol(ans-mol(s1+s2-s3)); 48 } 49 printf("%lld",ans); 50 return 0; 51 } 52 //19940417= 2848631*7 phi(19940417)=17091779