hdu1695(莫比乌斯反演模板)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1695
题意: 对于 a, b, c, d, k . 有 x 属于 [a, b], y 属于 [c, d], 求 gcd(x, y) = k 的 x, y 的对数 . 其中 a = b = 1 .
注意: (x, y), (y, x) 算一种情况 .
思路: 莫比乌斯反演
可以参考一下: http://blog.csdn.net/lixuepeng_001/article/details/50577932
公式: F(n) = sigma f(d) , 其中 (n | d) ==> f(n) = sigma u(d / n) * F(d) , 其中 (n | d) .
其中 u 为莫比乌斯函数, 定义为:
若 d = 1, 则 u(d) = 1;
若 d = p1p2...pk, 则 u(d) = (-1)^k , 其中 pi 为互异质数;
其他 u(d) = 0;
对于 gcd(x, y) = k, 显然有 gcd(x / k, y / k) = 1 . 那么原题等价于求 gcd(x, y) = 1, 其中 x 属于 (1, b / k), y 属于 (1, d / k) .
然后定义 f(n) 表示满足条件的 gcd(x,y) = n 的 (x, y) 对数,
再定义 F(n) 表示满足 n | gcd(x,y) 的 (x, y) 对数, 即 gcd(x, y) % n = 0 的x, y对数 .
那么我们要求的就是 f(1) .
通过前面推理不难发现 F(x) = n / x * m / x, 其中 n = b / k, m = d / k;
那么 f(1) = sigma u(d / 1) * F(d), 其中 1 | d 且 d <= min(n, m);
即 f(1) = sigma u(d) * (n / d) * (m / d);
关于去重: f'(1) = sigma u(d) * (n / d) * (n / d), 其中 n 为 m, n中的最小值, 则 sol = f(1) - f'(1) / 2;
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define ll long long 5 using namespace std; 6 7 const int MAXN = 1e6 + 10; 8 9 bool check[MAXN]; 10 int mu[MAXN], prime[MAXN]; 11 12 void Moblus(void){ 13 memset(check, false, sizeof(check)); 14 int tot = 0; 15 mu[1] = 1; 16 for(int i = 2; i < MAXN; i++){ 17 if(!check[i]){ 18 prime[tot++] = i; 19 mu[i] = -1; 20 } 21 for(int j = 0; j < tot && i * prime[j] < MAXN; j++){ 22 check[i * prime[j]] = true; 23 if(i % prime[j] == 0){ 24 mu[i * prime[j]] = 0; 25 break; 26 }else mu[i * prime[j]] = -mu[i]; 27 } 28 } 29 } 30 31 int main(void){ 32 int t, a, b, c, d, k; 33 Moblus(); 34 scanf("%d", &t); 35 for(int i = 1; i <= t; i++){ 36 scanf("%d%d%d%d%d", &a, &b, &c, &d, &k); 37 if(k == 0){ 38 printf("Case %d: 0\n", i); 39 continue; 40 } 41 if(b > d) swap(b, d); 42 b /= k; 43 d /= k; 44 ll ans1 = 0, ans2 = 0; 45 for(int j = 1; j <= b; j++){ 46 ans1 += (ll)mu[j] * (b / j) * (d / j); 47 } 48 for(int j = 1; j <= b; j++){ 49 ans2 += (ll)mu[j] * (b / j) * (b / j); 50 } 51 printf("Case %d: %lld\n",i, ans1 - (ans2 >> 1)); 52 } 53 return 0; 54 }
因为对于某些连续的 i 会有 b / i, d / i 是相同的, 这种情况可以通过前缀和优化一下 .
优化代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define ll long long 5 using namespace std; 6 7 const int MAXN = 1e5 + 10; 8 9 bool check[MAXN]; 10 int prime[MAXN], mu[MAXN], sum[MAXN]; 11 12 void Moblus(void){ 13 memset(check, false, sizeof(check)); 14 int tot = 0; 15 mu[1] = 1; 16 sum[1] = 1; 17 for(int i = 2; i < MAXN; i++){ 18 if(!check[i]){ 19 prime[tot++] = i; 20 mu[i] = -1; 21 } 22 for(int j = 0; j < tot && prime[j] * i < MAXN; j++){ 23 check[i * prime[j]] = true; 24 if(i % prime[j] == 0){ 25 mu[i * prime[j]] = 0; 26 break; 27 }else mu[i * prime[j]] = -mu[i]; 28 } 29 sum[i] = sum[i - 1] + mu[i]; 30 } 31 } 32 33 ll solve(int b, int d){ 34 ll ans = 0; 35 for(int i = 1, la = 0; i <= b; i = la + 1){ 36 la = min(b / (b / i), d / (d / i)); 37 ans += (ll)(sum[la] - sum[i - 1]) * (b / i) * (d / i); 38 } 39 return ans; 40 } 41 42 int main(void){ 43 Moblus(); 44 int t, a, b, c, d, k; 45 scanf("%d", &t); 46 for(int i = 1; i <= t; i++){ 47 scanf("%d%d%d%d%d", &a, &b, &c, &d, &k); 48 if(k == 0){ 49 printf("Case %d: 0\n", i); 50 continue; 51 } 52 b /= k; 53 d /= k; 54 if(b > d) swap(b, d); 55 ll ans1 = solve(b, d); 56 ll ans2 = solve(b, b); 57 printf("Case %d: %lld\n", i, ans1 - (ans2 >> 1)); 58 } 59 return 0; 60 }
就本题而言时间从 31ms 优化到了 15 ms .