1,[POI2007]ZAP-Queries
~~~题面~~~
题解: 首先列出式子:$$ans = sum_{i = 1}^{n}sum_{j = 1}^{m}[gcd(i, j) == d]$$
$$[gcd(i, j) == d] = [gcd(lfloor{frac{i}{d}}
floor,lfloor{frac{j}{d}}
floor) == 1]$$
所以原式 $$Rightarrow quad sum_{i = 1}^{lfloor{frac{n}{d}}
floor}sum_{j = 1}^{lfloor{frac{m}{d}}
floor}[gcd(i, j)==1]$$
$$Rightarrow quad sum_{i = 1}^{n}sum_{j = 1}^{m}sum_{d|gcd(i, j)}mu(d)$$
因为$mu(d)$会被统计到当且仅当$d | i quad and quad d | j$,即$d | gcd(i, j)$
那么考虑将满足条件的i和j两两搭配,组成的方案数就是$mu(d)$会被统计到的次数,
也就是$mu(d)$会被统计到$lfloor{frac{n}{d}}
floorlfloor{frac{m}{d}}
floor$次
$$Rightarrow quad ans=sum_{i=1}^{min(n,m)}{mu(d)lfloor{frac{n}{d}}
floorlfloor{frac{m}{d}}
floor}$$
然后观察到$lfloor{frac{n}{d}}
floorlfloor{frac{m}{d}}
floor$中有很多小段$lfloor{frac{n}{d}}
floorlfloor{frac{m}{d}}
floor$乘积是固定的,也就是$lfloor{frac{n}{d}}
floor$和$lfloor{frac{m}{d}}
floor$同时为一个固定的值,因此我们可以用整数分块优化
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 55000 5 int t, n, m, d; 6 int prime[AC], mu[AC], sum[AC], tot; 7 bool z[AC]; 8 9 inline int read() 10 { 11 int x = 0;char c = getchar(); 12 while(c > '9' || c < '0') c = getchar(); 13 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 14 return x; 15 } 16 17 void getprime() 18 { 19 int now; 20 mu[1] = 1; 21 for(R i = 2; i <= 50000; i++) 22 { 23 if(!z[i]) prime[++tot] = i, mu[i] = -1; 24 for(R j = 1; j <= tot; j++) 25 { 26 now = prime[j]; 27 if(now * i > 50000) break; 28 // printf("!!!%d %d ", now, i); 29 z[now * i] = true; 30 if(!(i % now)) break; 31 mu[i * now] = -mu[i]; 32 } 33 } 34 for(R i = 1; i <= 50000; i++) 35 sum[i] = mu[i] + sum[i - 1]; 36 } 37 38 int ans; 39 40 void work() 41 { 42 int pos; 43 t = read(); 44 for(R i = 1; i <= t; i++) 45 { 46 n = read(), m = read(), d = read(); 47 n /= d, m /= d; 48 int b = min(n, m); 49 ans = 0; 50 for(R j = 1; j <= b; j = pos + 1) 51 { 52 pos = min(n / (n / j), m / (m / j)); 53 ans += (sum[pos] - sum[j-1]) * (n / j) * (m / j); 54 } 55 printf("%d ", ans); 56 } 57 } 58 59 int main() 60 { 61 // freopen("in.in", "r", stdin); 62 getprime(); 63 work(); 64 // fclose(stdin); 65 return 0; 66 }
2,[HAOI2011]Problem b
~~~题面~~~
题解: 这题相比上题多出了两个限制条件,同时对上下限都有限制,那么此时可以用一个容斥来求。
设$cal(n,m)$表示满足$x le n$和$y le m$且$gcd(x, y) = d$的点对个数,
那么$$ans = cal(b, d) - cal(a - 1, d) - cal(b, c - 1) + cal(a - 1, c - 1);$$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 55000 5 #define LL long long 6 int a, b, c, d, tot, k, t, ans; 7 int prime[AC], mu[AC], sum[AC]; 8 bool z[AC]; 9 10 inline int read() 11 { 12 int x = 0; char c = getchar(); 13 while(c > '9' || c < '0') c = getchar(); 14 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 15 return x; 16 } 17 18 void pre() 19 { 20 int now; 21 mu[1] = 1; 22 for(R i = 2; i <= 50000; i++) 23 { 24 if(!z[i]) prime[++tot] = i, mu[i] = -1;//质数的mu值肯定都是-1 25 for(R j = 1; j <= tot; j++) 26 { 27 now = prime[j]; 28 if(now * i > 50000) break; 29 z[now * i] = true; 30 if(!(i % now)) break; 31 mu[i * now] = -mu[i];//error这里的mu是由mu[i]得来的啊!!! 32 } 33 } 34 for(R i = 1; i <= 50000; i++) sum[i] = sum[i - 1] + mu[i]; 35 } 36 37 int cal(int n, int m) 38 { 39 n /= k, m /= k; 40 int b = min(n, m), pos, rnt = 0; 41 for(R i = 1; i <= b; i = pos + 1) 42 { 43 pos = min(n / (n / i), m / (m / i)); 44 rnt += (sum[pos] - sum[i-1]) * (n / i) * (m / i); 45 } 46 return rnt; 47 } 48 //ans = cal(b, d) - cal(a - 1, d) - cal(b, c - 1) + cal(a - 1 , c - 1),相当于容斥 49 50 void work() 51 { 52 t = read(); 53 for(R i = 1; i <= t; i++) 54 { 55 a = read(), b = read(), c = read(), d = read(), k = read(); 56 ans = cal(b, d) - cal(a - 1, d) - cal(b, c - 1) + cal(a - 1, c - 1); 57 printf("%d ", ans); 58 } 59 } 60 61 int main() 62 { 63 // freopen("in.in", "r", stdin); 64 pre(); 65 work(); 66 // fclose(stdin); 67 return 0; 68 }