[SDOI2015]约数个数和
题目大意:
$q(q\leq50000)$组询问,对于给定的$n,m(n,m\leq50000)$,求$\displaystyle\sum_{i=1}^n\sum_{j=1}^m\tau(ij)$。
思路:
$\displaystyle\tau(ij)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)==1]$。
$$
\begin{align*}
原式&=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[\gcd(x,y)==1]\\
&=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}\sum_{d|\gcd(x,y)}\mu(d)\\
&=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|\gcd(i,j)}\mu(d)\tau(\frac id)\tau(\frac jd)\\
&=\sum_{d=1}^{\min(n,m)}\sum_{i=1}^{\lfloor\frac nd\rfloor}\tau(i)\sum_{j=1}^{\lfloor\frac md\rfloor}\tau(j)
\end{align*}
$$
其中$\sum\tau(i)$可以线性筛预处理,$d$可以数论分块枚举,单次询问复杂度$O(\sqrt n)$。
1 #include<cstdio> 2 #include<cctype> 3 #include<algorithm> 4 typedef long long int64; 5 inline int getint() { 6 register char ch; 7 while(!isdigit(ch=getchar())); 8 register int x=ch^'0'; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 const int N=50001,M=5134; 13 bool vis[N]; 14 int p[M],mu[N],tau[N],cnt[N],sum_mu[N],sum_tau[N]; 15 inline void sieve() { 16 mu[1]=tau[1]=sum_mu[1]=sum_tau[1]=1; 17 for(register int i=2;i<N;i++) { 18 if(!vis[i]) { 19 p[++p[0]]=i; 20 mu[i]=-1; 21 cnt[i]=1; 22 tau[i]=2; 23 } 24 for(register int j=1;j<=p[0]&&i*p[j]<N;j++) { 25 vis[i*p[j]]=true; 26 if(i%p[j]==0) { 27 mu[i*p[j]]=0; 28 cnt[i*p[j]]=cnt[i]+1; 29 tau[i*p[j]]=tau[i]/cnt[i*p[j]]*(cnt[i*p[j]]+1); 30 break; 31 } 32 mu[i*p[j]]=-mu[i]; 33 cnt[i*p[j]]=1; 34 tau[i*p[j]]=tau[i]*2; 35 } 36 sum_mu[i]=sum_mu[i-1]+mu[i]; 37 sum_tau[i]=sum_tau[i-1]+tau[i]; 38 } 39 } 40 int main() { 41 sieve(); 42 for(register int T=getint();T;T--) { 43 const int n=getint(),m=getint(),lim=std::min(n,m); 44 int64 ans=0; 45 for(register int l=1,r;l<=lim;l=r+1) { 46 r=std::min(n/(n/l),m/(m/l)); 47 ans+=(int64)(sum_mu[r]-sum_mu[l-1])*sum_tau[n/l]*sum_tau[m/l]; 48 } 49 printf("%lld\n",ans); 50 } 51 return 0; 52 }