【hdu4135】【hdu2841】【hdu1695】一类通过容斥定理求区间互质的方法
【HDU4135】Co-prime
题意
给出三个整数N,A,B。问在区间[A,B]内,与N互质的数的个数。其中N<=10^9,A,B<=10^15。
分析
容斥定理的模板题。可以通过容斥定理求出[1,n]与x互质的数的个数。方法是先将x进行质因子分解,然后对于每个质因子pi,[1,n]内可以被pi整除的数目为n/pi。可以通过容斥定理解决逆命题,既[1,n]与x不互质的数目。n/p1+n/p2+n/p3-n/p1p2-n/p1p3-n/p2p3+n/p1p2p3。既奇数是加,偶数是减。具体的做法一般是通过二进制枚举来进行。质因子分解N的时间复杂度是O(sqrt(N)),然后枚举质因子的时间复杂度是O(2^(num)),其中num是质因子的数目。我们知道,这个num一般来说是非常小的,所以这个算法的时间复杂度是非常优秀的。
然后对于这个题,我们求出[1,B]与N互质的个数再减去[1,A]互质的个数。
code如下
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 typedef long long LL; 8 int T,N,num; 9 LL A,B; 10 int a[100]; 11 void prime(int n){ 12 num=0; 13 for(int i=2;i*i<=n;i++){ 14 if((n%i)==0){ 15 num++; 16 a[num]=i; 17 while((n%i)==0){ 18 n/=i; 19 } 20 } 21 } 22 if(n>1){ 23 num++; 24 a[num]=n; 25 } 26 return ; 27 } 28 LL solve(LL r,int n){ 29 prime(n); 30 LL res=0; 31 for(int i=1;i<(1<<num);i++){ 32 int kk=0; 33 LL div=1; 34 for(int j=1;j<=num;j++){ 35 if(i&(1<<(j-1))){ 36 kk++; 37 div*=a[j]; 38 } 39 } 40 if(kk%2) 41 res+=r/div; 42 else 43 res-=r/div; 44 } 45 return r-res; 46 } 47 int main(){ 48 scanf("%d",&T); 49 for(int t=1;t<=T;t++){ 50 scanf("%lld%lld%d",&A,&B,&N); 51 LL ans=solve(B,N)-solve(A-1,N); 52 printf("Case #%d: %lld\n",t,ans); 53 } 54 return 0; 55 }
【HDU2841】visible tree
题意
有一片树林m*n,从(1,1)开始,每个整数点都有一棵树。famer站在点(0,0),问他能看见的树有几棵。其中n,m<=100000.
分析
先来看有哪些树没有办法被看到。对于点(xi,yi),当xi和yi可以同时被一个大于1的整数k整除,则点(xi,yi)无法被看到。也就是说,当且仅当这个点的横纵坐标互质时,才可以被看到。也就是说求[1,n]和[1,m]内有多少互质的点。到此为止,本题已经转换为上一个题的形式。然后枚举[1,m]作为x,然后[1,n]作为区间,按照上题方案进行求解。
code 如下
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 typedef long long LL; 8 int T,n,m,num; 9 int a[20]; 10 void prime(int x){ 11 num=0; 12 for(int i=2;i*i<=x;i++){ 13 if(x%i==0){ 14 num++; 15 a[num]=i; 16 while(x%i==0&&x){ 17 x/=i; 18 } 19 } 20 } 21 if(x>1){ 22 num++; 23 a[num]=x; 24 } 25 return; 26 } 27 LL solve(LL r,int x){ 28 LL res=0; 29 for(int i=1;i<(1<<num);i++){ 30 LL k=0,aa=1; 31 for(int j=1;j<=num;j++){ 32 if(i&(1<<(j-1))){ 33 k++; 34 aa*=a[j]; 35 } 36 } 37 if(k%2) 38 res+=r/aa; 39 else 40 res-=r/aa; 41 } 42 return r-res; 43 } 44 int main(){ 45 scanf("%d",&T); 46 47 for(int t=1;t<=T;t++){ 48 scanf("%d%d",&n,&m); 49 if(n>m)swap(n,m); 50 LL ans=0; 51 52 for(int i=1;i<=n;i++){ 53 prime(i); 54 ans+=solve(m,i); 55 //cout<<solve(m,i)<<endl; 56 } 57 printf("%lld\n",ans); 58 } 59 return 0; 60 }
【HDU1695】GCD
题意
给出a,b,c,d,k,其中保证a=1,c=1。问在区间[a,b]和区间[c,d]内有多少不同的对gcd(x,y)=k。
分析
如果gcd(x,y)=k,则显然gcd(x/k,y/k)=1,既互质。我们令b<d,然后b/=k,d/=k。则题目转化为在区间[1,b]和区间[1,d]有多少不同对互质。
有没有感觉和上面那道题很像?就一个不同点,这个题目求的是不同对。所以不可以按照上面这个题直接进行枚举。对于区间[1,b],我们可以直接枚举求和欧拉函数。对于区间[b+1,d]我们可以按照上面的方法通过容斥定理进行求解。
code如下
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 typedef long long LL; 8 9 int T,a,b,c,d,k; 10 LL ans; 11 int euler(int n){ //返回euler(n) 12 int res=n,aa=n; 13 for(int i=2;i*i<=aa;i++){ 14 if(aa%i==0){ 15 res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出 16 while(aa%i==0) aa/=i; 17 } 18 } 19 if(aa>1) res=res/aa*(aa-1); 20 return res; 21 } 22 int num; 23 int pri[20]; 24 void prime(int x){ 25 num=0; 26 for(int i=2;i*i<=x;i++){ 27 if(x%i==0){ 28 num++; 29 pri[num]=i; 30 while(x%i==0){ 31 x/=i; 32 } 33 } 34 } 35 if(x>1){ 36 num++; 37 pri[num]=x; 38 } 39 } 40 LL solve(LL r,int x){ 41 LL res=0; 42 for(int i=1;i<(1<<num);i++){ 43 int k=0,aa=1; 44 for(int j=1;j<=num;j++){ 45 if(i&(1<<(j-1))){ 46 k++; 47 aa*=pri[j]; 48 } 49 } 50 if(k%2) 51 res+=r/aa; 52 else 53 res-=r/aa; 54 } 55 return r-res; 56 } 57 int main(){ 58 scanf("%d",&T); 59 for(int t=1;t<=T;t++){ 60 scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); 61 if(k==0){ 62 printf("Case %d: 0\n",t); 63 continue; 64 } 65 if(b>d)swap(b,d); 66 b/=k,d/=k; 67 ans=0; 68 for(int i=1;i<=b;i++){ 69 ans+=euler(i); 70 } 71 for(LL i=b+1;i<=d;i++){ 72 prime(i); 73 ans+=solve(b,i); 74 } 75 printf("Case %d: %lld\n",t,ans); 76 } 77 return 0; 78 }