【莫比乌斯反演】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 }

 

posted @ 2015-06-24 00:41  CyanNode  阅读(200)  评论(0编辑  收藏  举报