題解【bzoj2301 [HAOI2011]Problem b】
Description
求有多少個數對 \((x,y)\) ,滿足$ a \leq x \leq b$ ,\(c \leq y \leq d\) ,且 \(\gcd(x,y) = k\),\(\gcd(x,y)\)函式為 \(x\) 和 \(y\) 的最大公約數。多組詢問。\(a,b,c,d,k,T \leq 50000\)
Solution
莫比烏斯反演的經典題目QAQ
首相將問題轉化成字首上的問題。即需要求出 有多少個數對 \((x,y)\) ,滿足$ 1 \leq x \leq a$ ,\(1 \leq y \leq b\) ,且 \(\gcd(x,y) = k\)。如果能夠快速算出來這個,容斥一下就可以求出最後答案。
考慮這個怎麼求,開始推式子。這個東西顯然就是
\[\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=k]\]
把 \(k\) 提出來可得
\[\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}[\gcd(i, j)=1]\]
然後把後面這個 \([\gcd(i,j)=1]\) 反演掉,得
\[\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum\limits_{d|\gcd(i,j)}\mu(d)\]
把 \(d\) 搞到前面來,得到
\[\sum\limits_{d=1}^{\lfloor \frac{n}{k} \rfloor} \mu(d)\lfloor \frac{n}{kd} \rfloor\lfloor \frac{m}{kd} \rfloor\]
好了,這個玩意可以預處理出 \(\mu\) 得字首和然後分塊完事。
Code
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 50000; int k, cnt, p[N + 50], mu[N + 50], flag[N + 50], sum[N + 50]; inline void prework() { flag[1] = mu[1] = 1; for(int i = 2; i <= N; i++) { if(!flag[i]) { p[++cnt] = i; mu[i] = -1; } for(int j = 1; j <= cnt && i * p[j] <= N; j++) { flag[i * p[j]] = 1; if(i % p[j] == 0) { mu[i * p[j]] = 0; break; } mu[i * p[j]] = mu[i] * -1; } } for(int i = 1; i <= N; i++) sum[i] = sum[i - 1] + mu[i]; } inline ll calc(int n, int m) { if(n > m) swap(n, m); ll ret = 0; for(int l = 1, r; l <= n / k; l = r + 1) { r = min(n / (n / l), m / (m / l)); ret += 1ll * (n / (l * k)) * (m / (l * k)) * (sum[r] - sum[l - 1]); } return ret; } int main() { int T; prework(); scanf("%d", &T); while(T--) { int a, b, c, d; scanf("%d %d %d %d %d", &a, &b, &c, &d, &k); printf("%lld\n", calc(a - 1, c - 1) - calc(b, c - 1) - calc(d, a - 1) + calc(b, d)); } return 0; }