P3455 [POI2007]ZAP-Queries
題面:**
太長了,懶得粘了
懵逼烏斯反演入門題。
首先,我們要求的是這個柿子:
\(\displaystyle \sum_{i=1}^{n}\sum_{j=1}^{m} [gcd(i,j) == k]\) (這裡的 \(n,m\) 即題面中的 \(a,b\) 。 \(k\) 為題面中的 \(d\))
為了省事(偷懶) 我們在這裡預設 \(n \leq m\)
按套路,提取一個 \(k\) 出來變成:
\(\displaystyle\sum_{i=1}^{n\over k} \sum_{j=1}^{m\over k} [gcd(i,j) == 1]\)
由莫比烏斯反演可以得到一個等式:
\(\displaystyle [gcd(i,j) == 1] = \sum_{d=1,d\mid i,d\mid j}^{n} \mu(d)\)
把這個柿子回代到原來的柿子可得:
\(\displaystyle\sum_{i=1}^{n\over k} \sum_{j=1}^{m\over k}\sum_{d\mid i, d\mid k} \mu(d)\)
先列舉一下 \(d\) 變成:
\(\displaystyle\sum_{d=1}^{n\over k} \mu(d) \sum_{i=1}^{n\over k} [d\mid i] \sum_{j = 1}^{m\over k}[d\mid k]\)
在變換一下可以變成:
\(\displaystyle\sum_{d=1}^{n\over k} \mu(d) \lfloor{n\over kd}\rfloor\lfloor{m\over kd}\rfloor\)
這就是我們最後的總柿子。
在對 \(\mu\) 函式值求個字首和,在套個數論分塊即可。
Code
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; #define LL long long const int N = 5e4+10; int t,n,m,d,tot; int prime[N],mu[N],sum[N]; bool check[N]; inline int read() { int s = 0,w = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();} return s * w; } void YYCH() { mu[1] = 1; for(int i = 2; i <= N-5; i++)//篩 mu 函式 { if(!check[i]) { prime[++tot] = i; mu[i] = -1; } for(int j = 1; j <= tot && i * prime[j] <= N-5; j++) { check[i * prime[j]] = 1; if(i % prime[j] == 0) { mu[i * prime[j]] = 0; break; } else { mu[i * prime[j]] = -mu[i]; } } } for(int i = 1; i <= N-5; i++)//字首和 { sum[i] = sum[i-1] + mu[i]; } } LL slove(int n,int m,int d) { LL res = 0; for(int l = 1,r; l <= n/d; l = r+1)//數論分塊 { r = min(n/(n/l),m/(m/l)); res += 1LL * (sum[r] - sum[l-1]) * (n/l/d) * (m/l/d); } return res; } int main() { t = read(); YYCH(); while(t--) { n = read(); m = read(); d = read(); if(n > m) swap(n,m); printf("%lld\n",slove(n,m,d)); } return 0; }