【莫比乌斯反演+容斥】BZOJ2301-[HAOI2011]Problem b(成为权限狗的第一题纪念!)

【题目大意】

对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

【思路】

“怎么又是你系列……”思路和分块方法分别参见:🐈🐩 ←岛国输入法超级好用啊wwww

最后容斥一下:ans=g(b,d,k)−g(a−1,d,k)−g(b,c−1,k)+g(a−1,c−1,k)

【错误点】

我干了件很愚蠢的事,一上来就把a,b,c,d全部除以k,再按上述容斥的方式传入了子函数。注意,是(a-1)/k,不是a/k-1。

 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=0x7fffffff;
 9 int a,b,c,d,k;
10 int miu[MAXN],prime[MAXN],pnum;
11 ll miusum[MAXN];
12 
13 void getmiu(int maxn)
14 {
15     memset(miusum,0,sizeof(miusum));
16     miu[1]=miusum[1]=1;
17     for (int i=2;i<maxn;i++) miu[i]=-INF;
18     for (int i=2;i<maxn;i++)
19     {
20         if (miu[i]==-INF)
21         {
22             miu[i]=-1;
23             prime[++pnum]=i;
24         }
25         for (int j=1;j<=pnum;j++)
26         {
27             if (i*prime[j]>=maxn) break;
28             if (i%prime[j]==0) miu[i*prime[j]]=0;
29                 else miu[i*prime[j]]=-miu[i];
30             
31         }
32         miusum[i]=miusum[i-1]+miu[i];
33     }
34 }
35 
36 ll solve(int b,int d,int k)
37 {
38     b/=k;d/=k;
39     int ub=min(b,d),pos;
40     ll ret=0;
41     for (int i=1;i<=ub;i=pos+1)
42     {
43         pos=min(b/(b/i),d/(d/i));
44         ret+=(ll)(miusum[pos]-miusum[i-1])*(ll)(b/i)*(ll)(d/i);
45     }
46     return ret;
47 }
48 
49 int main()
50 {
51     int T;
52     getmiu(MAXN-1);
53     scanf("%d",&T);
54     while (T--)
55     {
56         scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
57         ll ans=solve(b,d,k)-solve(a-1,d,k)-solve(c-1,b,k)+solve(a-1,c-1,k);
58         printf("%lld\n",ans);
59     }
60 }

 

posted @ 2016-07-27 15:23  iiyiyi  阅读(...)  评论(...编辑  收藏