容斥原理&&莫比乌斯专题

A题:A - Eddy's爱好   HDU - 2204 

具体思路:如果是求n中,为平方数的有多少个,那么答案肯定是sqrt(n),同理,如果是三次根号的话,那么答案肯定是n的三分之一次方。然后继续按照这个思路来,对于1e18次方的数,最多就是2的64次方,也就是说我们最多枚举大小不超过63的素数就可以了,然后还需要考虑一种情况,比如说6的时候,被素数2算了一遍,然后又被素数3算了一遍,这个地方会有重复的计算,又因为2^(3*5*7)已经超过2的60次方了,所以我们只需要考虑三部分就可以了。

 AC代码(1):

 1 #include <iostream>
 2 #include <string>
 3 #include <deque>
 4 #include <stack>
 5 #include<cmath>
 6 #include <algorithm>
 7 #include<cstring>
 8 #include<stdio.h>
 9 #include<map>
10 using namespace std;
11 # define ll long long
12 # define inf 0x3f3f3f3f
13 # define ll_inf 1ll<<60
14 const int maxn = 1e6+100;
15 ll prim[]= {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61};
16 ll cal(ll t)
17 {
18     ll sum=1;
19     for(int i=0;; i++)
20     {
21         ll tmp=pow(t,1.0/(prim[i]*1.0));
22         if(tmp<2)
23             break;
24         sum+=tmp-1;
25         for(int j=i+1;; j++)
26         {
27             tmp=pow(t,1.0/(prim[i]*prim[j]*1.0));
28             if(tmp<2)
29                 break;
30             sum-=tmp-1;
31             for(int k=j+1;; k++)
32             {
33                 tmp=pow(t,1.0/(prim[i]*prim[j]*prim[k]*1.0));
34                 if(tmp<2)
35                     break;
36                 sum+=tmp-1;
37             }
38         }
39     }
40     return sum;
41 }
42     int main()
43     {
44         ll tmp;
45         while(cin>>tmp)
46         {
47             cout<<cal(tmp)<<endl;
48         }
49         return 0;
50     }

 

 AC代码(2):

我们可以利用莫比乌斯系数进行简化计算,在上一个版本中,我们是按照奇加偶减的原则来进行的,同样这个计算的过程可以通过莫比乌斯中的mu函数来直接算出,每次相乘的系数是-mu[i]

 1 #include <iostream>
 2 #include <string>
 3 #include <deque>
 4 #include <stack>
 5 #include<cmath>
 6 #include <algorithm>
 7 #include<cstring>
 8 #include<stdio.h>
 9 #include<map>
10 using namespace std;
11 # define ll long long
12 # define inf 0x3f3f3f3f
13 # define ll_inf 1ll<<60
14 const int maxn = 100+100;
15 ll mu[maxn*100],prim[maxn*100],check[maxn*100];
16 int tot=0;
17 void getmu()
18 {
19     mu[1]=1;
20     for(int i=2; i<maxn; i++)
21     {
22         // cout<<i<<endl;
23         if(!check[i])
24         {
25             prim[tot++]=i;
26             mu[i]=-1;
27         }
28         for(int j=0; j<tot; j++)
29         {
30             // cout<<i<<endl;
31             if(prim[j]>maxn/i)
32             {
33                 break;
34             }
35             check[i*prim[j]]=1;
36             if(i%prim[j]==0)
37             {
38                 mu[i*prim[j]]=0;
39                 break;
40             }
41             else
42             {
43                 mu[i*prim[j]]=-mu[i];
44             }
45         }
46     }
47     // cout<<1<<endl;
48 }
49 int main()
50 {
51     getmu();
52     ll tmp;
53     while(cin>>tmp)
54     {
55         ll sum=1;
56         for(ll  i=2; i<=64; i++)
57         {
58             sum-=mu[i]*(ll)(pow(tmp*1.0,1.0/i)-1);
59         }
60         cout<<sum<<endl;
61     }
62     return 0;
63 }

 

B题:

题目链接:

B - How many integers can you find

 HDU - 1796 

题目大意:输入n,m.然后再输入m个数,问你1-(n-1)中,是这些数中任意个倍数的有多少?

具体思路:我们可以运用容斥的原理,最终答案等于sum=(能被一个数整除)-(能被两个数整除)+(能被三个数整除)-------.然后按照这个思路,我们运用二进制枚举直接暴力就可以了。 (int会爆掉)

AC代码:

  1 #include<iostream>
  2 #include<stack>
  3 #include<iomanip>
  4 #include<cstring>
  5 #include<string>
  6 #include<cmath>
  7 #include<algorithm>
  8 #include<stdio.h>
  9 using namespace std;
 10 # define int long long
 11 const int maxn = 100+10;
 12 int a[maxn];
 13 int gcd(int a,int b){
 14 if(a<b)swap(a,b);
 15 return a%b==0?b:gcd(b,a%b);
 16 }
 17 int lcm(int a,int b)
 18 {
 19     return a*b/gcd(a,b);
 20 }
 21 int cal(int n,int t)
 22 {
 23     int ans=0;
 24     int maxstate=(1<<t)-1;
 25     for(int i=1; i<=maxstate; i++)
 26     {
 27         int ind=0;
 28         int tmp=1;
 29         for(int j=0; j<=t; j++)
 30         {
 31             if((1<<j)&i)
 32             {
 33                 ind++;
 34                 tmp=lcm(tmp,a[j+1]);
 35             }
 36         }
 37         ans-=mu[tmp]*(n/tmp);//其实这个地方也可以直接通过一个莫比乌斯中的mu函数求,但是这样的话mu函数就需要求的比较大,所以对于这个题还是建议下面这个方法,按照容斥原理。
 38         if(ind&1)
 39         {
 40             ans+=n/tmp;
 41         }
 42         else
 43             ans-=n/tmp;
 44     }
 45     return ans;
 46 }
 47 signed main()
 48 {
 49     int n,m;
 50     while(cin>>n>>m)
 51     {
 52         int tot=0;
 53         int tmp;
 54         n--;
 55         for(int i=1; i<=m; i++)
 56         {
 57             cin>>tmp;
 58             if(!tmp)
 59                 continue;
 60             a[++tot]=tmp;
 61         }
 62         int ans=cal(n,tot);
 63         cout<<ans<<endl;
 64     }
 65  //   return 0;
 66 }
 67 //ll mu[maxn+100],prim[maxn+100],check[maxn+100];
 68 //void getmu()
 69 //{
 70 //    int tot=0;
 71 //    mu[1]=1;
 72 //    for(int i=2; i<maxn; i++)
 73 //    {
 74 //        if(!check[i])
 75 //        {
 76 //            prim[tot++]=i;
 77 //            mu[i]=-1;
 78 //        }
 79 //        for(int j=0; j<tot; j++)
 80 //        {
 81 //            if(prim[j]>maxn/i)
 82 //            {
 83 //                break;
 84 //            }
 85 //            check[i*prim[j]]=1;
 86 //            if(i%prim[j]==0)
 87 //            {
 88 //                mu[i*prim[j]]
 89 //                    =0;
 90 //                break;
 91 //            }
 92 //            else
 93 //            {
 94 //                mu[i*prim[j]]=-mu[i];
 95 //            }
 96 //        }
 97 //    }
 98 //}
 99 //int main()
100 //{
101 //    getmu();
102 //    ll n,m;
103 //    while(~scanf("%lld %lld",&n,&m))
104 //    {
105 //        ll minn=1ll<<60,maxx=0;
106 //        ll tmp=0;
107 //        n--;
108 //        for(int i=1; i<=m; i++)
109 //        {
110 //            scanf("%lld",&tmp);
111 //            maxx=max(maxx,tmp);
112 //            minn=min(minn,tmp);
113 //        }
114 //        ll sum=1;
115 //        for(int i=minn; i<=maxx; i++)
116 //        {
117 //            sum-=mu[i]*(n/i);
118 //        }
119 //        cout<<sum<<endl;
120 //           printf("%lldn")
121 //    }
122 //}

 

C题:

C - Visible Trees

 HDU - 2841  

题目大意:给你一个n*m的图,你站在(0.0)这个点上,这个田地上从)(1,1)这个点开始有树,如果一个树和另外树在一条一个直线,这个时候后面那棵树就会看不到,然后问你这个人能看到多少树?

具体思路:首先来分析一波,如果一个树能被这个人看到,也就是说不会有别的树挡住当前这棵树,再分析到这棵树的坐标,也就说这个树的坐标不会被分解,也就说这个树的坐标x和y互素,分析到这里就可以了。我们只需要判断给定的(x,y),(1,x)和(1,y)这段区间互素的数有多少就可以了。莫比乌斯反演、、

计算过程:

然后对于当前这个题,选择(1,b),(1,d) 中满足gcd(x,y)==1的对数,(1<=x<=b),(1<=y<=d) .

也就是说 gcd(x,y)==1满足的对数.

然后再开始分析一波:

我们令f(k)为满足(a,b),(c,d)中的gcd为1的对数.然后F(1)就是满足(a,b),(c,d)中的gcd为k的倍数的对数.

F(k)就等于(b/k)*(d/k).

f(1)= \sum mu(d)*F(n)))).

AC代码:

  1 #include<iostream>
  2 #include<stack>
  3 #include<iomanip>
  4 #include<cstring>
  5 #include<string>
  6 #include<cmath>
  7 #include<algorithm>
  8 #include<stdio.h>
  9 using namespace std;
 10 const int maxn = 100000+100;
 11 # define int long long
 12 int a[maxn];
 13 int mu[maxn+100],prim[maxn+100],check[maxn+100];
 14 void getmu()
 15 {
 16     int tot=0;
 17     mu[1]=1;
 18     for(int i=2; i<maxn; i++)
 19     {
 20         if(!check[i])
 21         {
 22             prim[tot++]=i;
 23             mu[i]=-1;
 24         }
 25         for(int j=0; j<tot; j++)
 26         {
 27             if(prim[j]>maxn/i)
 28             {
 29                 break;
 30             }
 31             check[i*prim[j]]=1;
 32             if(i%prim[j]==0)
 33             {
 34                 mu[i*prim[j]]
 35                     =0;
 36                 break;
 37             }
 38             else
 39             {
 40                 mu[i*prim[j]]=-mu[i];
 41             }
 42         }
 43     }
 44 }
 45 signed main()
 46 {
 47     getmu();
 48     int T;
 49     int n,m;
 50     cin>>T;
 51     while(T--)
 52     {
 53         cin>>n>>m;
 54         int sum=0;
 55         int minn=min(n,m);
 56         for(int i=1; i<=minn; i++)
 57         {
 58             sum+=mu[i]*(n/i)*(m/i);
 59         }
 60         cout<<sum<<endl;
 61     }
 62     return 0;
 63 }
 64 D题:
 65 
 66 #include<iostream>
 67 #include<stack>
 68 #include<iomanip>
 69 #include<cstring>
 70 #include<string>
 71 #include<cmath>
 72 #include<algorithm>
 73 #include<stdio.h>
 74 using namespace std;
 75 const int maxn = 1e6+100;
 76 # define ll long long
 77 ll a[maxn];
 78 ll mu[maxn+100],prim[maxn+100],check[maxn+100];
 79 void getmu()
 80 {
 81     int tot=0;
 82     mu[1]=1;
 83     for(int i=2; i<maxn; i++)
 84     {
 85         if(!check[i])
 86         {
 87             prim[tot++]=i;
 88             mu[i]=-1;
 89         }
 90         for(int j=0; j<tot; j++)
 91         {
 92             if(prim[j]>maxn/i)
 93             {
 94                 break;
 95             }
 96             check[i*prim[j]]=1;
 97             if(i%prim[j]==0)
 98             {
 99                 mu[i*prim[j]]
100                     =0;
101                 break;
102             }
103             else
104             {
105                 mu[i*prim[j]]=-mu[i];
106             }
107         }
108     }
109 }
110 int main()
111 {
112     getmu();
113     int T;
114     cin>>T;
115     int Case=0;
116     while(T--)
117     {
118         ll  x1,y1,x2,y2;
119         cin>>x1>>y1>>x2>>y2;
120         ll k;
121         cin>>k;
122         if(k==0){
123                         printf("Case %d: %lld\n",++Case,0);
124         }
125         else {
126 
127         y1/=k;
128         y2/=k;
129         ll sum1=0,sum2=0;
130         ll minn=min(y1,y2);
131         for(int i=1; i<=minn; i++)
132         {
133             sum1+=mu[i]*(y1/i)*(y2/i);
134             sum2+=mu[i]*(minn/i)*(minn/i);
135         }
136         printf("Case %d: %lld\n",++Case,sum1-sum2/2);
137         }
138     }
139     return 0;
140 }
141  

 

posted @ 2018-12-25 20:47  Let_Life_Stop  阅读(333)  评论(0编辑  收藏  举报