题目链接:GCD
解题思路:一开始搜莫比乌斯反演的时候找到的,结果不会,后来看discuss里面说容斥原理+欧拉函数,那么要求gcd(x, y) = k就可以转化为gcd(x / k, y / k) = 1,那么就是互素的数队。欧拉函数表示的就是小于n并且和n互素的数。所以对于区间[1,a][1, b](a < b)来说, 小于a的部分就是直接加欧拉函数值就可以了,那么大于a小于等于b的部分就要容斥原理。设Q是这个部分的一个数,那么对于区间[1,a],只要里面的数不包含Q的质因数就互素,所以我们要找出来这些数。
设Q = I * J * K 我们要减去[1,a]内I, J, K的倍数,就是(a / I + a / J + a / k),发现里面有些数减多了,就是IJ的倍数,JK的倍数,IK的倍数,所以再加回来,又发现加多了,再减去IJK的倍数。所以我们观察发现由偶数个不同的质因数就要减去,奇数个就要加上,这就是容斥原理
RE原因,一直没看K可以等于0,除0报错
TLE原因,对于每一组数据来说都要重新加一遍欧拉函数和,并且对于每一个数都要sqrt(n)来分解质因数,太慢了,所以就预先打出表来。
WA原因,我之前计算的话总是莫名其妙少1,发现我少算了(1,1) 这个点,我就天真的加了1,一直WA,其实应该把euler[1] = 1,这样就对了,但是我的程序一直都没有计算过(1,1)并且所有数据除了k=0特判过了,其他的都必须有(1,1),所以不知为什么。
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<algorithm> #define ll __int64 #define MAX 100100 using namespace std; bool isPrime[MAX]; int prime[MAX], eulur[MAX], cnt; vector<int> p[MAX]; ll falei[MAX]; ll ans; int fa[50], tt; void init(){ ans = 0; } void Eulur(){ int i, j, k; falei[1] = eulur[1] = 1; for(i = 2; i < MAX; i++){ if(!isPrime[i]){ prime[cnt++] = i; eulur[i] = i - 1; p[i].push_back(i); } for(j = 0; j < cnt && i * prime[j] < MAX; j++){ isPrime[i * prime[j]] = true; if(i % prime[j] == 0){ eulur[i * prime[j]] = eulur[i] * prime[j]; for(k = 0; k < p[i].size(); k++){ p[i * prime[j]].push_back(p[i][k]); } break; } else{ eulur[i * prime[j]] = eulur[i] * (prime[j] - 1); for(k = 0; k < p[i].size(); k++){ p[i * prime[j]].push_back(p[i][k]); } p[i * prime[j]].push_back(prime[j]); } } falei[i] = falei[i - 1] + eulur[i]; } eulur[1] = 1; } int get(int x, int y){ int i, j, k, ret = 0; for(i = 1; i < (1 << p[x].size()); i++){ int tmp = i, sum = 1, amt = 0; j = 0; while(tmp){ if(tmp & 1){ sum *= p[x][j]; amt++; } tmp >>= 1; j++; } if(sum <= y && sum != 0){ ret += y / sum * (amt % 2 ? 1 : -1); } } return y - ret; } int main(){ int i, j, k; int a, b, c, d, e; int t, cas = 1; Eulur(); scanf("%d", &t); while(t--){ scanf("%d%d%d%d%d", &a, &b, &c, &d, &e); init(); if(e == 0){ printf("Case %d: %d\n", cas++, 0); continue; } b /= e, d /= e; a = min(b, d), c = max(b, d); ans += falei[a]; for(i = a + 1; i <= c; i++){ ans += get(i, a); } printf("Case %d: %I64d\n", cas++, ans); } return 0; }