bzoj 2820 YY的GCD - 莫比乌斯反演 - 线性筛
Description
神犇YY虐完数论后给傻×kAc出了一题给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对kAc这种
傻×必然不会了,于是向你来请教……多组输入
Input
第一行一个整数T 表述数据组数接下来T行,每行两个正整数,表示N, M
Output
T行,每行一个整数表示第i组数据的结果
Sample Input
2
10 10
100 100
10 10
100 100
Sample Output
30
2791
2791
HINT
T = 10000
N, M <= 10000000
题目大意 (题目太简洁不需要大意)。
根据常用套路,我们有
显然TLE,为了更好的分段计算,所以考虑把后面两个向下取整取整的分数挪出来。于是得到了下面这个优美的式子:
现在就来考虑后面那个求和。
考虑莫比乌斯函数的性质和p为质数。
1)当T存在一个质因子的指数超过2,显然对答案贡献为0
2)当T存在超过1个质因子的指数为2,显然对答案贡献为0
3)当T只存在1个质因子的指数为2,那么当且仅当p的值为这个质因子时,对答案才有贡献,这个贡献为-1的不同质因子次幂。
4)当T的质因子的指数均为1,那么对答案的贡献为T的质因子个数乘-1的不同质因子个数减一 次幂。
显然可以用线性筛预处理出这个的值的前缀和,这样就可以做到单次查询根号级别。
至于如何用线性筛预处理,我记录了一个enable表示是否存在1个质因子的指数大于等于2和defp表示不同的质因子的个数。详细的过程请看代码。
Code
1 /** 2 * bzoj 3 * Problem#2820 4 * Accepted 5 * Time:4708ms 6 * Memory:141912k 7 */ 8 #include <bits/stdc++.h> 9 #ifndef WIN32 10 #define Auto "%lld" 11 #else 12 #define Auto "%I64d" 13 #endif 14 using namespace std; 15 typedef bool boolean; 16 17 #define LL long long 18 19 const int limit = 1e7; 20 21 int num = 0; 22 int prime[1000000]; 23 boolean vis[limit + 1]; 24 boolean enable[limit + 1]; 25 LL g[limit + 1]; 26 int defp[limit + 1]; 27 inline void Euler() { 28 memset(vis, false, sizeof(vis)); 29 memset(enable, true, sizeof(enable)); 30 g[0] = g[1] = 0; 31 for(int i = 2; i <= limit; i++) { 32 if(!vis[i]) prime[num++] = i, g[i] = 1, defp[i] = 1; 33 for(int j = 0; j < num && i * 1LL * prime[j] <= limit; j++) { 34 int c = i * prime[j]; 35 vis[c] = true; 36 if(i % prime[j]) { 37 defp[c] = defp[i] + 1; 38 enable[c] = enable[i]; 39 g[c] = (enable[i]) ? ((defp[c]) * ((defp[c] & 1) ? (1) : (-1))) : (-g[i]); 40 } else { 41 defp[c] = defp[i]; 42 enable[c] = false; 43 g[c] = (enable[i]) ? ((defp[c] & 1) ? (-1) : (1)) : (0); 44 break; 45 } 46 } 47 } 48 for(int i = 2; i <= limit; i++) 49 g[i] += g[i - 1]; 50 // cout << num << endl; 51 // for(int i = 1; i <= 20; i++) 52 // cout << i << " " << g[i] << " " << defp[i] << endl; 53 } 54 55 int n, m; 56 inline void init() { 57 scanf("%d%d", &n, &m); 58 } 59 60 inline void solve() { 61 if(n > m) swap(n, m); 62 LL res = 0; 63 for(int i = 1, j; i <= n; i = j + 1) { 64 j = min(n / (n / i), m / (m / i)); 65 res += (n / j) * 1LL * (m / j) * (g[j] - g[i - 1]); 66 } 67 printf(Auto"\n", res); 68 } 69 70 int T; 71 int main() { 72 Euler(); 73 scanf("%d", &T); 74 while(T--) { 75 init(); 76 solve(); 77 } 78 return 0; 79 }