【莫比乌斯反演+分块】BZOJ1101-[POI2007]Zap

【题目大意】

对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d。

【思路】

前面的思路同HDU1695

不过不同的是这道题中(a,b)和(b,a)算作同一种情况,不需要再减去重复的情况。

这道题运用了分块加快效率。我们可以注意到是相同的,b'(b'/i)b表示和当前商相同的最后一位[b'/d],d'(d'/i)同理,pos为两者中较小的一个。我们预处理Miu的前缀和,就可以将一样的*(sum[pos]-pos[k-1])即可!

??BZOJ一定要用printf%lld,cout会RE??

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 typedef long long ll;
 7 const int MAXN=50000+50;
 8 const int INF=0x7ffffff;
 9 int a,b,d;
10 int miu[MAXN];
11 int miu_sum[MAXN];
12 
13 void get_miu(int maxn)
14 {
15     int prime[MAXN],pnum=0;
16     memset(miu_sum,0,sizeof(miu_sum));
17     miu[1]=miu_sum[1]=1;
18     for (int i=2;i<maxn;i++) miu[i]=-INF;
19     for (int i=2;i<maxn;i++)
20     {
21         if (miu[i]==-INF)
22         {
23             miu[i]=-1;
24             prime[++pnum]=i;
25         }
26         for (int j=1;j<=pnum;j++)
27         {
28             if (i*prime[j]>=maxn) break;
29             if (i%prime[j]==0) miu[i*prime[j]]=0;
30                 else miu[i*prime[j]]=-miu[i];
31         }
32         miu_sum[i]=miu_sum[i-1]+miu[i];
33     }
34 }
35 
36 ll get_ans(int a,int b,int d)
37 {
38     int ub=min(a,b),pos;
39     ll ans=0;
40     for (int i=1;i<=ub;i=pos+1)
41     {
42         pos=min(a/(a/i),b/(b/i));
43         ans+=(ll)(miu_sum[pos]-miu_sum[i-1])*(a/i)*(b/i);
44     }
45     return ans;
46 }
47 
48 int main()
49 {
50     int T;
51     scanf("%d",&T);
52     get_miu(MAXN-1); 
53     for (int i=0;i<T;i++)
54     {
55         scanf("%d%d%d",&a,&b,&d);
56         a/=d;b/=d;
57         printf("%lld\n",get_ans(a,b,d));
58     }
59 }

 

posted @ 2016-07-06 09:34  iiyiyi  阅读(213)  评论(0编辑  收藏  举报