【莫比乌斯反演】BZOJ1101 [POI2007]zap
Description
回答T组询问,有多少组gcd(x,y)=d,x<=a, y<=b。T, a, b<=4e5。
Solution
显然对于gcd=d的,应该把a/d b/d,然后转为gcd=1计算
计算用莫比乌斯反演相信大家都会
关键是有T组询问n^2会T
于是有这样一个优化可以做到每次sqrt(n)
每一次是ret+=mu[i]*(n/i)*(m/i)
可是除法向下取整所以会导致很多i的(n/i)*(m/i)一样
具体来说,向下取整得到的结果一定是约数所以对于(n/i)最多2sqrt(n)种
那么(n/i)*(m/i)放一起也就4sqrt(n)种
这个序列一定是不上升的,所以考虑对所有的(n/i)*(m/i)视为一块相同的一起算
那么肯定要记录下mu[i]的前缀和
如何快速得到每一块的l和r?
每一块的r肯定要么n%i==0要么m%i==0
于是用pos=min(n/(n/i),m/(m/i)) 定位
当然pos+1就是下一块的l了
Code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int maxn=5e4+5; 6 7 int flag[maxn],prime[maxn],cnt; 8 int mu[maxn],sum[maxn]; 9 10 int getmu(){ 11 mu[1]=1; 12 for(int i=2;i<maxn;i++){ 13 if(!flag[i]){ 14 prime[++cnt]=i; 15 mu[i]=-1; 16 } 17 for(int j=1;i*prime[j]<maxn&&j<=cnt;j++){ 18 flag[i*prime[j]]=1; 19 if(i%prime[j]==0){ 20 mu[i*prime[j]]=0; 21 break; 22 } 23 mu[i*prime[j]]=-mu[i]; 24 } 25 } 26 for(int i=1;i<maxn;i++) 27 sum[i]=sum[i-1]+mu[i]; 28 } 29 30 int cal(int n,int m){ 31 int ret=0,pos; 32 if(n>m) swap(n,m); 33 for(int i=1;i<=n;i=pos+1){ 34 pos=min(n/(n/i),m/(m/i)); 35 ret+=(sum[pos]-sum[i-1])*(n/i)*(m/i); 36 } 37 return ret; 38 } 39 40 int main(){ 41 int T,a,b,d; 42 scanf("%d",&T); 43 getmu(); 44 45 while(T--){ 46 scanf("%d%d%d",&a,&b,&d); 47 a/=d,b/=d; 48 printf("%d\n",cal(a,b)); 49 } 50 return 0; 51 }