莫比烏斯反演 + 整除分塊。ide
關於整除分塊:spa
對於 i <= x <= n / (n / i) 的 x
code
有 n / x = n / iblog
證實略,記住就好(滑稽)get
1 for(int i = 1, j; i <= n; i = j + 1) { 2 j = n / (n / i); 3 // j = std::(m / (m / i), n / (n / i)); 4 // 操做區間[i, j] 5 }
接下來看題:要求[a,b] ~ [c,d]中 gcd == k 的個數。it
按照套路,設io
f(x) = sum[x == g],F(x) = sum[x | g]event
發現離解題還差的很遠....class
下一步:發現能夠用相似二維前綴和的方式來求解。cli
問題轉化爲求 n, m 中 gcd == k
而後就很熟悉了...
而後就T了.......
注意到F(x) = (n / x) * (m / x),能夠整除分塊。
有個結論就是:
而後就能夠作了。
我本人不能想到這個結論,因而提供另外一種思路:
gcd(i, j) == k => gcd(i / k, j / k) == 1
在這二者之間能夠創建一一對應的映射。
因而咱們把m, n都/k,就成了求f(1),這個時候就是裸的整除分塊了。
1 #include <cstdio> 2 #include <algorithm> 3 const int N = 50010; 4 5 int sum[N], miu[N], p[N], top, k; 6 bool vis[N]; 7 8 inline int F(int x, int n, int m) { 9 return (n / x) * (m / x); 10 } 11 12 inline void getmiu(int n = 50005) { 13 miu[1] = 1; 14 for(int i = 2; i <= n; i++) { 15 if(!vis[i]) { 16 p[++top] = i; 17 miu[i] = -1; 18 } 19 for(int j = 1; j <= top && i * p[j] <= n; j++) { 20 vis[i * p[j]] = 1; 21 if(i % p[j] == 0) { 22 break; 23 } 24 miu[i * p[j]] = -miu[i]; 25 } 26 } 27 for(int i = 1; i <= n; i++) { 28 sum[i] = sum[i - 1] + miu[i]; 29 } 30 return; 31 } 32 33 inline int solve(int n, int m) { 34 n /= k; 35 m /= k; 36 int c = m > n ? n : m; 37 int ans = 0; 38 /*for(int i = 1; i <= c; i++) { 39 //ans += miu[i] * F(i, n, m); 40 ans += miu[i] * (n / i) * (m /i); 41 }*/ 42 for(int i = 1, j; i <= c; i = j + 1) { // [i, j] 43 j = std::min(n / (n / i), m / (m / i)); 44 ans += (n / i) * (m / i) * (sum[j] - sum[i - 1]); 45 } 46 return ans; 47 } 48 49 int main() { 50 int n, a, b, c, d; 51 getmiu(); 52 scanf("%d", &n); 53 while(n--) { 54 scanf("%d%d%d%d%d", &a, &b, &c, &d, &k); 55 int ans = solve(b, d) - solve(b, c - 1) - solve(a - 1, d) + solve(a - 1, c - 1); 56 printf("%d\n", ans); 57 } 58 59 return 0; 60 }