【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
",t,ans);
53     }
54 return 0;
55 }
View Code

【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
",ans);
58     }
59 return 0;
60 }
View Code
 

【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
",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
",t,ans);
76     }
77 return 0;
78 }
View Code

  

原文地址:https://www.cnblogs.com/LQLlulu/p/9051510.html