BZOJ3994: [SDOI2015]約數個數和
【傳送門:BZOJ3994】
簡要題意:
給出n,m,設d(x)為x的約數個數,求$\sum_{i=1}^{n}\sum_{j=1}^{m}d(i*j)$
題解:
莫比烏斯反演,設n<m
yy一下可以發現$d(i*j)=\sum_{x|i}\sum_{y|j}1[gcd(x,y)==1]$
然後原式就等於$$\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}1[gcd(x,y)==1]$$
先枚舉x,y,得到$$\sum_{x=1}^{n}\sum_{y=1}^{m}\frac{n}{i}*\frac{m}{j}[gcd(x,y)==1]$$
因為$\sum_{d|x}\mu(d)=[x==1]$
所以得到$$\sum_{x=1}^{n}\sum_{y=1}^{m}\frac{n}{i}*\frac{m}{j}\sum_{d|gcd(i,j)}\mu(d)$$
先枚舉d,得到$$\sum_{d=1}^{n}\mu(d)\sum_{x=1}^{n}[d|x]\sum_{y=1}^{m}[d|y]\frac{n}{x}*\frac{m}{y}$$
$$\sum_{d=1}^{n}\mu(d)\sum_{x=1}^{\frac{n}{d}}\frac{n}{d*x}*\sum_{y=1}^{\frac{m}{d}}\frac{m}{d*y}$$
顯然最前面一項可以預處理,設$S(x)=\sum_{i=1}^{x}\frac{x}{i}$,得到$$\sum_{d=1}^{n}\mu(d)*S(\frac{n}{d})*S(\frac{m}{d})$$
S(x)可以$O(n*\sqrt{n})$預處理,但是實際上可以在線性篩的時候順便處理
因為我們發現$S(x)=\sum_{i=1}^{x}d(i)$,只要求出d(i)就可以了
參考代碼:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; typedef long long LL; int miu[51000],prime[51000],v[51000]; LL d[51000];int c[51000]; void pre(int n) { int m=0;miu[1]=d[1]=c[1]=1; for(int i=2;i<=n;i++) { if(v[i]==0) { v[i]=i;c[i]=1;d[i]=2; prime[++m]=i; miu[i]=-1; } for(int j=1;j<=m;j++) { if(prime[j]>v[i]||prime[j]>n/i) continue; v[i*prime[j]]=prime[j]; if(i%prime[j]==0) miu[i*prime[j]]=0,d[i*prime[j]]=d[i]/(c[i]+1)*(c[i]+2),c[i*prime[j]]=c[i]+1; else miu[i*prime[j]]=-miu[i],d[i*prime[j]]=d[i]*d[prime[j]],c[i*prime[j]]=1; } } for(int i=1;i<=n;i++) miu[i]+=miu[i-1],d[i]+=d[i-1]; } int main() { pre(50000); int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m);if(n>m) swap(n,m); LL ans=0; for(int i=1,j;i<=n;i=j+1) { j=min(n/(n/i),m/(m/i)); ans+=(LL)(miu[j]-miu[i-1])*d[n/i]*d[m/i]; } printf("%lld\n",ans); } return 0; }
BZOJ3994: [SDOI2015]約數個數和