GCD HDU - 1695
考察:容斥原理+埃氏筛+质因数分解
思路:
gcd(x,y) = k 等价于gcd(x/k,y/k) = 1.设 u = x/k,v= y/k.找出范围内u与v互质的对数.
这里可以用欧拉函数做,枚举1~d/k每一个数i.如果i在b/k范围内就是欧拉函数值,如果>b/k就是该欧拉函数值-b/k~i中与gcd(i,j)!=1的数.
那么问题是如何找gcd(i,j)!=1的数.由之前的Hankson趣味题可知,公约数是i,j所有质因数的指数取最小值的乘积.如果gcd!=1,那么gcd能分解为质数.所以将i分解为质因数乘积.筛去b/k~i中,该质因数的倍数,但是存在是多个质因数的倍数的数,所以需要用到容斥原理.最后将答案累加即可.
注意:存在k==0的情况,此时答案为0
与上面思路很像的思路2:
同样是枚举,但是是枚举u,1~b/k中计算求出每个数的质因数,用容斥原理筛去倍数.答案是d/k-i-容斥原理求出的倍数个数
蒟蒻是参考了思路2的思路
关于分解质因数:
这里用了埃氏筛的思想,有点妙,本蒟蒻一直以为只能筛处质数再一个个分解..233
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int N = 100010; 7 int prime[N],cnt,a,b,c,d,k; 8 bool st[N]; 9 vector<int> v[N]; 10 void GetPrime(int n) 11 { 12 st[0] = st[1] = 1; 13 for(int i=2;i<=n;i++) 14 { 15 if(st[i]) continue; 16 v[i].push_back(i);//i可被自己分解 17 for(int j=i*2;j<=n;j+=i) 18 { 19 st[j] = 1; 20 v[j].push_back(i);//j可被哪些质数分解 21 } 22 } 23 } 24 int main() 25 { 26 GetPrime(1e5); 27 int T,kcase= 0; 28 scanf("%d",&T); 29 while(T--) 30 { 31 scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); 32 printf("Case %d: ",++kcase); 33 if(!k||k>b||k>d) { printf("0\n"); continue;} 34 b/=k,d/=k; 35 if(b>d) swap(d,b);//将x视为小的数 36 ll ans = d; 37 for(int i=2;i<=b;i++) //ans应为所有数-与i不互质的数 38 { 39 int sz = v[i].size();//枚举每个1~b的数 40 ll t = 0;//i+1到d中倍数的数目 41 for(int j=1;j<1<<sz;j++)//枚举i的质因数的倍数的集合 42 { 43 int cnt = 0; ll tmp = 1; 44 for(int k=0;k<sz;k++)//选哪些集合 45 { 46 if(j>>k&1) 47 { 48 cnt++; 49 if(v[i][k]*tmp>d)//如果范围超过不计入 50 { 51 tmp = -1; 52 break; 53 } 54 tmp*=v[i][k]; 55 } 56 } 57 if(tmp!=-1)//ans = i+1~d的是i质因数倍数的个数 58 { 59 if(cnt&1) t+=d/tmp-i/tmp; 60 else t-=d/tmp-i/tmp; 61 } 62 } 63 ans += (ll)d-i-t; 64 } 65 printf("%lld\n",ans); 66 } 67 return 0; 68 }
2021.06.12 学了Mobius函数,可以利用整除分块与前缀和的形式优化求与i互质的数的个数.时间复杂度可以优化到2*T*sqrt(n).但是注意这道题是(x,y),(y,x)是计入同一个.但是只会在(1,min(b,d)里计入相同的,所以将(1,min(b,d))的ans/2再减去即可.