BZOJ1101 [POI2007] Zap
1101: [POI2007]Zap
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2690 Solved: 1137
[Submit][Status][Discuss]
Description
FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a
,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。
Input
第一行包含一个正整数n,表示一共有n组询问。(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个
正整数,分别为a,b,d。(1<=d<=a,b<=50000)
Output
对于每组询问,输出到输出文件zap.out一个正整数,表示满足条件的整数对数。
Sample Input
2
4 5 2
6 4 3
4 5 2
6 4 3
Sample Output
3
2
//对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(
6,3),(3,3)。
2
//对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(
6,3),(3,3)。
HINT
Source
【题解】
莫比乌斯反演。
下面转自http://blog.csdn.net/regina8023/article/details/43876363(侵删)
先把题目转化为求x<=a/d,y<=b/d且gcd(x,y)=1的(x,y)有多少对。
而莫比乌斯函数有一个性质是
这里的n=1与gcd(x,y)=1相似。
(下面引用自 iwtwiioi)
如果直接枚举d来做会TLE,但是我们发现a'/d的值在d等于好多值得时候都是相同的。
比如a'=100,那么d在[34,50]之间a'/d都是2。
那么我们可以把连续的一段d一起来算(分块):
设a'/d=x,那么最后一个a'/d=x的d=a'/x,所以这段连续的区间就是[d,a'/(a'/d)]
结合b'/d,取个min就可以了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdlib> 5 #define min(a, b) ((a) < (b) ? (a) : (b)) 6 7 inline void read(long long &x) 8 { 9 x = 0;char ch = getchar(), c = ch; 10 while(ch < '0' || ch > '9')c = ch, ch = getchar(); 11 while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar(); 12 if(c == '-')x = -x; 13 } 14 15 const long long MAXN = 50000 + 10; 16 17 long long miu[MAXN << 1], t, n, m, k, bprime[MAXN << 1], prime[MAXN << 1], tot, v; 18 19 void make_miu() 20 { 21 miu[1] = 1; 22 for(register long long i = 2;i <= MAXN;++ i) 23 { 24 if(!bprime[i]) 25 { 26 prime[++tot] = i; 27 miu[i] = -1; 28 } 29 for(register long long j = 1;j <= tot && i * prime[j] <= MAXN;++ j) 30 { 31 bprime[i * prime[j]] = 1; 32 if(i % prime[j] == 0) 33 { 34 miu[i * prime[j]] = 0; 35 break; 36 } 37 miu[i * prime[j]] = -miu[i]; 38 } 39 } 40 for(register long long i = 1;i <= MAXN;++ i)miu[i] += miu[i - 1]; 41 } 42 43 int main() 44 { 45 read(t); 46 make_miu(); 47 for(;t;-- t) 48 { 49 read(n), read(m), read(k); 50 n /= k, m /= k; 51 long long ma = min(n, m); 52 register long long ans = 0, x, y, end; 53 for(register long long d = 1;d <= ma;++ d) 54 { 55 //当前n/d = x 最后一个d = n/x x:[d, n/(n/x)] 56 x = n/d, y = m/d; 57 end = min(n/x, min(m/y, ma)); 58 ans += (miu[end] - miu[d - 1]) * x * y; 59 d = end; 60 } 61 printf("%lld\n", ans); 62 } 63 return 0; 64 }