题解 P2260 【[清华集训2012]模积和】
P2260 [清华集训2012]模积和解题报告:
更好的阅读体验
双倍经验:P2834 能力测验
题意
题意:求
数据范围:
分析
这是一道简单的推式子题,但是实现比较恶心。
首先不妨设
然后可以用容斥将题目拆成两个部分:
将两个求和拆开:
因为取模很难搞,所以可以用一个性质
将括号拆开,可以得到:
此时我们的复杂度已经是
要做这道题需要一个简单的技巧——整除分块。
我们很容易发现很多
通过简单的计算可以得到,对于每个起点为
主要步骤,对于每一个块
代码:
inline long long sum1(long long x){
return x*(x+1)%mod*inv2%mod;
}
l=1,sum=n*n%mod;
while(l<=n){
r=n/(n/l);
sum=(sum-(sum1(r)-sum1(l-1)+mod)%mod*(n/l)%mod+mod)%mod;
l=r+1;
}
同样,
考虑求
可以用类似的方法,将上面代码中的r=n/(n/l);
改为r=min(n/(n/l),m/(m/l));
,然后就可以直接求值了!
且慢,虽然前三项
这里有一个简单的结论:
inline long long sum2(long long x){
return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
}
l=1,tmp3=0;
while(l<=n){
long long a,b,c;
r=min(n/(n/l),m/(m/l));
a=(r-l+1)*n%mod*m%mod;
b=(sum1(r)-sum1(l-1)+mod)%mod*((n/l)*m%mod+(m/l)*n%mod)%mod;
c=(sum2(r)-sum2(l-1)+mod)%mod*(n/l)%mod*(m/l)%mod;
tmp3=(tmp3+a-b+c+mod)%mod;
l=r+1;
}
代码
- 记得开long long
- 取模很恶心,如果错了多半是这种原因
- 因为三个整数相乘会爆long long,因此除法用逆元实现(逆元提前求得)
#include<stdio.h>
const long long mod=19940417,inv2=9970209,inv6=3323403;
long long i,j,k,m,n,l,r,ans,tmp1,tmp2,tmp3;
inline long long min(long long a,long long b){
return a<b? a:b;
}
inline void swp(long long &a,long long &b){
a+=b,b=a-b,a-=b;
}
inline long long sum1(long long x){
return x*(x+1)%mod*inv2%mod;
}
inline long long sum2(long long x){
return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
}
int main(){
scanf("%lld%lld",&n,&m);
if(n>m)
swp(n,m);
l=1,tmp1=n*n%mod;
while(l<=n){
r=n/(n/l);
tmp1=(tmp1-(sum1(r)-sum1(l-1)+mod)%mod*(n/l)%mod+mod)%mod;
l=r+1;
}
l=1,tmp2=m*m%mod;
while(l<=m){
r=m/(m/l);
tmp2=(tmp2-(sum1(r)-sum1(l-1)+mod)%mod*(m/l)%mod+mod)%mod;
l=r+1;
}
l=1,tmp3=0;
while(l<=n){
long long a,b,c;
r=min(n/(n/l),m/(m/l));
a=(r-l+1)*n%mod*m%mod;
b=(sum1(r)-sum1(l-1)+mod)%mod*((n/l)*m%mod+(m/l)*n%mod)%mod;
c=(sum2(r)-sum2(l-1)+mod)%mod*(n/l)%mod*(m/l)%mod;
tmp3=(tmp3+a-b+c+mod)%mod;
l=r+1;
}
ans=(tmp1*tmp2%mod-tmp3+mod)%mod;
printf("%lld\n",ans);
return 0;
}
简单的证明
证明:
(这个式子可以用简单构造法与数学归纳法证明,由于大部分题解都是用的数学归纳法,且用数学归纳法读者自证不难,因此这里使用简单构造法)
由于有这样一个式子:
然后乘上
构造一下:
把它们全部展开:
发现括号里的很多项都可以抵消:
证毕。