[bzoj2820]YY的GCD
来自FallDream的博客,未经允许,请勿转载,谢谢。
------------------------------------------------------------------------
$T<=10000$组数据,每次给定$n,m\leqslant 10^{7}$,求$1\leqslant x\leqslant n,1\leqslant y\leqslant m,gcd(x,y)是质数$的(x,y)有多少对。
首先枚举质数p,很容易得到式子$$Ans=\sum_{p=1}^{n}\sum_{i=1}^{\lfloor \frac{n}{p}\rfloor}\sum_{i=1}^{\lfloor \frac{m}{p}\rfloor}\sum_{d|i,d|j}\mu(d)$$
然后我们从d看待这个式子,得到$$Ans=\sum_{p=1}^{n}\sum_{d=1}^{\lfloor \frac{n}{p}\rfloor}\mu(d)\lfloor \frac{n}{pd} \rfloor\lfloor \frac{m}{pd} \rfloor$$
这样还是太麻烦了,我们设T=pd,枚举T.对于每个T,$\lfloor \frac{n}{T}\rfloor$和$\lfloor \frac{m}{T}\rfloor$不变,而$\mu(d)$则只有在作为它的因数的时候出现,所以得到式子
$$Ans=\sum_{T=1}^{n}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{p|T}\mu(\frac{T}{p})$$
然后$\sum_{p|T}\mu(\frac{T}{p})$这东西直接枚举质数,质数数量$n/logn$,每个质数均摊$logn$,预处理复杂度O(n),然后每个询问可以在$\sqrt(n)$时间内完成。
#include<iostream> #include<cstdio> #define ll long long #define MN 10000000 using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } int mu[MN+5],f[MN+5],s[MN],num=0; bool b[MN+5]; ll calc(int n,int m) { ll sum=0; for(int i=1,last;i<=n;i=last+1) { last=min(n/(n/i),m/(m/i)); sum+=1LL*(f[last]-f[i-1])*(n/i)*(m/i); } return sum; } int main() { mu[1]=1; for(register int i=2;i<=MN;i++) { if(!b[i]) { mu[i]=-1;f[i]=1; s[++num]=i; } for(register int j=1;s[j]*i<=MN;j++) { b[s[j]*i]=1; if(i%s[j]==0) break; else mu[s[j]*i]=-mu[i]; } } for(int i=1;i<=num;i++) { for(register int j=s[i]<<1;j<=MN;j+=s[i]) f[j]+=mu[j/s[i]]; } for(int i=2;i<=MN;i++) f[i]+=f[i-1]; int T=read(); while(T--) { int n=read(),m=read(); if(n>m)swap(n,m); printf("%lld\n",calc(n,m)); } return 0; }