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