bzoj1101: [POI2007]Zap(莫比乌斯反演)

mac2022-06-30  131

原题链接

题目描述:FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。

输入格式:第一行包含一个正整数n,表示一共有n组询问。(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个正整数,分别为a,b,d。(1<=<=a,b<=50000)

输出格式:对于每组询问,输出到输出文件zap.out一个正整数,表示满足条件的整数对数。

输入样例: 2 4 5 2 6 4

输出样例: 3 2 //对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(6,3),(3,3)。

解析:挺裸的莫比乌斯反演题。    设\(f(x)=gcd为x的(x,y)的对数\)    设\(F(x)=gcd为x或x的倍数的(x,y)的对数\)    那么显然\(F(x)=\sum_{x|d}f(d)\)    所以\(f(x)=\sum_{x|d}μ(⌊\frac{d}{x}⌋)F(d)\)    而\(F(x)=⌊\frac{n}{x}⌋⌊\frac{m}{x}⌋\)    可得\(f(x)=\sum_{x|d}μ(⌊\frac{d}{x}⌋)⌊\frac{n}{d}⌋⌊\frac{m}{d}⌋\)    枚举\(⌊\frac{d}{x}⌋\),则\(f(x)=\sum_{i=1}^{min(⌊\frac{n}{x}⌋,⌊\frac{m}{x}⌋)}μ(i)⌊\frac{n}{ix}⌋⌊\frac{m}{ix}⌋\)    这时就可以用整除分块计算了

代码如下:

#include<cstdio> #include<algorithm> #define ll long long using namespace std; const int maxn = 5e4 + 5; int n, mu[maxn], primi[maxn], tot, mark[maxn], k, prim[maxn]; ll sum[maxn], ans; int read(void) { char c; while (c = getchar(), c < '0' || c >'9'); int x = c - '0'; while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x; } void get_mu(void) { //筛mu函数 mu[1] = 1; mark[1] = 1; int lim = maxn - 5; for (int i = 2; i <= lim; ++ i) { if (!mark[i]) mu[i] = -1, prim[++ tot] = i; for (int j = 1; j <= tot && prim[j] * i <= lim; ++ j) { mark[prim[j] * i] = 1; if (i % prim[j] == 0) break; else mu[prim[j] * i] = -mu[i]; } } for (int i = 1; i <= lim; ++ i) sum[i] = sum[i - 1] + mu[i]; //维护mu函数的前缀和 } int main() { get_mu(); n = read(); while (n --) { int a = read(), b = read(), d = read(); a /= d; b /= d; ans = 0; int lim = min(a, b); //整数分块计算 for (int l = 1, r; l <= lim; l = r + 1) { r = min(a / (a / l), b / (b / l)); ans += (sum[r] - sum[l - 1]) * (a / l) *(b / l); } printf("%lld\n", ans); } return 0; }

转载于:https://www.cnblogs.com/Gaxc/p/10088151.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)