【洛谷3768】简单的数学题(莫比乌斯反演+杜教筛)
大致题意: 求\(\sum_{i=1}^n\sum_{j=1}^nijgcd(i,j)\%p\)。
前置技能
关于这道题目,我们首先需要了解以下知识:
知道这些,你就可以对这题的做法有一定的理解了。
推式子
首先,按照常见的套路,我们可以枚举\(gcd(i,j)=d\),得到这样一个式子:
然后,我们可以进行一个比较简单的转化,将中括号里面的\(d\)提出:
不难发现\(d^3\)与\(i,j\)无关,于是我们可以将其提前:
然后我们可以枚举\(gcd(i,j)=p\),并对原式做一遍莫比乌斯反演,可以得到这样一个式子:
然后将\(p\)提出,就变成了这样:
对于式子中的\((\sum_{i=1}^{\lfloor\frac n{d·p}\rfloor}i)(\sum_{j=1}^{\lfloor\frac n{d·p}\rfloor}j)\),可以化简得\((\frac{{\lfloor\frac n{d·p}\rfloor}·({\lfloor\frac n{d·p}\rfloor}+1)}2)^2\)。
那么原式就变成了这个样子:
如果设\(s=d·p\),则原式就变成了这个样子:
注意到式子中的\(d^2\)似乎可以约去,于是我们可以得到下面这个式子:
对于后面的一个式子\(\sum_{d|s}d\mu(\frac sd)\),用狄利克雷卷积的形式来表示就是这个样子:
而众所周知,\(id=I*\phi\),所以我们可以考虑将其代入,得到:
而\(\mu*I=e\)刚好约掉,所以这个式子的结果就是:
再代回原式就化简出来一个比较简单的式子了:
于是,我们可以考虑杜教筛筛出\(s^2\phi(s)\),然后除法分块求出前面一部分的值;而后面的\((\frac{{\lfloor\frac ns\rfloor}·({\lfloor\frac ns\rfloor}+1)}2)^2\)一项是可以\(O(1)\)计算的。
于是这道题就这样解决了。
代码
#include<bits/stdc++.h>
#define LL long long
#define sum_sqr(x) ((((x)%MOD)*(((x)+1)%MOD))%MOD*((((x)<<1)+1)%MOD)%MOD*InvSix%MOD)//求1^2+2^2+...+x^2的结果
#define sum_cube(x) ((((x)%MOD)*(((x)+1)%MOD)%MOD*InvTwo%MOD)*(((x)%MOD)*(((x)+1)%MOD)%MOD*InvTwo%MOD)%MOD)//求1^3+2^3+...+x^3的结果
#define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
#define Dec(x,y) ((x-=(y))<0&&(x+=MOD))
using namespace std;
LL n,MOD,InvTwo,InvSix;//分别用InvTwo和InvSix存储模MOD意义下2和6的逆元
inline LL Sum(LL x,LL y) {return Inc(x,y),x;}
inline LL Sub(LL x,LL y) {return Dec(x,y),x;}
class Class_DuSieve//杜教筛
{
private:
#define Size 5000000
int Prime_cnt,Prime[Size+5],phi[Size+5];bool IsNotPrime[Size+5];LL sum_phi[Size+5];
map<LL,LL> MemoryPhi;
public:
inline void Init()
{
register LL i,j;
for(phi[1]=1,i=2;i<=Size;++i)
{
if(!IsNotPrime[i]) phi[Prime[++Prime_cnt]=i]=i-1;
for(j=1;j<=Prime_cnt&&i*Prime[j]<=Size;++j)
if(IsNotPrime[i*Prime[j]]=true,i%Prime[j]) phi[i*Prime[j]]=phi[i]*(Prime[j]-1);else {phi[i*Prime[j]]=phi[i]*Prime[j];break;}
}
for(i=1;i<=Size;++i) sum_phi[i]=Sum(sum_phi[i-1],i*i%MOD*phi[i]%MOD);
}
inline LL GetPhi(LL x)
{
if(x<=Size) return sum_phi[x];
if(MemoryPhi[x]) return MemoryPhi[x];
register LL l,r,res=sum_cube(x);
for(l=2;l<=x;l=r+1) r=x/(x/l),Dec(res,Sub(sum_sqr(r),sum_sqr(l-1))*GetPhi(x/l)%MOD);//递归调用
return MemoryPhi[x]=res;
}
}DuSieve;
inline LL quick_pow(LL x,LL y,register LL res=1)//快速幂求逆元
{
for(;y;(x*=x)%=MOD,y>>=1) if(y&1) (res*=x)%=MOD;
return res;
}
int main()
{
register LL l,r,ans=0;
scanf("%lld%lld",&MOD,&n),DuSieve.Init(),InvTwo=quick_pow(2,MOD-2),InvSix=quick_pow(6,MOD-2);//预处理
for(l=1;l<=n;l=r+1) r=n/(n/l),Inc(ans,Sub(DuSieve.GetPhi(r),DuSieve.GetPhi(l-1))*sum_cube(n/l)%MOD);//除法分块求答案
return printf("%lld",ans),0;
}