bzoj[HAOI2011]Problem b(莫比烏斯反演)
阿新 • • 發佈:2018-12-08
題目描述:對於給出的n個詢問,每次求有多少個數對(x,y),滿足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函式為x和y的最大公約數。
輸入格式:第一行一個整數n,接下來n行每行五個整數,分別表示a、b、c、d、k
輸出格式:共n行,每行一個整數表示滿足要求的數對(x,y)的個數
輸入樣例:
2
2 5 1 5 1
1 5 1 5 2
輸出格式:
14
3
解析:似乎就是[POI2007]Zap的翻版,最後在計算時用類似二維字首和的思想計算答案即可。
具體的解析可以看我關於[POI2007]Zap的部落格,這裡就不在贅述。
POI2007
程式碼如下:
#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]; 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[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]; } ll calc(int a, int b) { //計算答案的函式 a /= k; b /= k; int lim = min(a, b); ll ans = 0; 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); } return ans; } int main() { get_mu(); n = read(); while (n --) { int a = read(), b = read(), c = read(), d = read(); k = read(); printf("%lld\n", calc(b, d) - calc(a - 1, d) - calc(b, c - 1) + calc(a - 1, c - 1)); //計算答案 } return 0; }