容斥原理专题

容斥原理适用于 n个集合,已知每个集合的元素个数,以及任意个集合的交的元素个数,求所有集合并的元素的个数等问题。

1.hdu 1659

    这道题的知识点涉及到欧拉函数,容斥原理。
    题意:给出(1,b),(1,d)两个区间,从中分别找出x,y,使得GCD(x,y)==k,要求得最多对数(x,y).
    首先假定d>b,要求的GCD(x,y)==k 相当于求得GCD(x/k,y/k)==1,故转化为求(1,b/k),(1,d/k)中最多互质的(x,y)对数
    b=b/k;d=d/k; 所以对于(1,d)中可分为两个区间 (1,b')+(b'+1,d) (由于b<d 故后一个区间可分为这两个区间,
    标记为(1,b'),(1,b'+1),b'==b )
    对于(1,b')中元素与(1,b)中元素互质的对数即为各个元素的欧拉函数之和。然而对于(b'+1,d)要求得互质对数则比较困难,
    故采用容斥原理:对于 b'+1<=i<=d
    区间中与i不互质的个数 = (区间中i的每个质因数的倍数个数)-(区间中i的每两个质因数乘积的倍数)+(区间中i的每3个质因数的成绩的倍数个数)-(区间中i的每个  质因数的乘积)+...
    至于为什么这样列出,请查看容斥原理,容斥原理实质上也算是个递推公式。
    所以最后解为 (1,b)中各个欧拉函数之和 + (b+1,d)中各个元素与区间(1,b)中元素互质对数之和

    附上代码:

   

  hanker A Accepted 9532 KB 187 ms G++ 1216 B 2012-07-30 17:06:25
View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 
 6 using namespace std;
 7 const int MAX=100005;
 8 long long eluer[MAX];
 9 int num[MAX];
10 int prime[MAX][20];
11 void Eluer()
12 {
13     memset(eluer,0,sizeof(eluer));
14     memset(num,0,sizeof(num));
15     eluer[1]=1;
16     for(int i=2;i<=MAX;i++)
17     {
18         if(!eluer[i])
19         {
20             for(int j=i;j<=MAX;j+=i)
21             {
22                 if(!eluer[j])
23                     eluer[j]=j;
24                 eluer[j]=eluer[j]*(i-1)/i;
25                 prime[j][num[j]++]=i;
26             }
27         }
28         eluer[i]+=eluer[i-1];
29     }
30 }
31 long long dfs(int k,int b,int x)
32 {
33     long long ret=0;
34     for(int i=k;i<num[x];i++)
35         ret+=b/prime[x][i]-dfs(i+1,b/prime[x][i],x);
36     return ret;
37 }
38 int main()
39 {
40     int t,a,b,c,d,k,kk=1;
41     long long ans;
42     scanf("%d",&t);
43     Eluer();
44     while(t--)
45     {
46         scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
47         if(k==0)
48         {
49             printf("Case %d: 0\n",kk++);
50         }
51         else
52         {
53         b/=k;d/=k;
54         if(b>d)
55             swap(b,d);
56         ans=eluer[b];
57         for(int i=b+1;i<=d;i++)
58             ans+=b-dfs(0,b,i);
59         printf("Case %d: %lld\n",kk++,ans);
60         }
61     }
62     return 0;
63 }

2.zoj 3556

  题意:给一个有N个元素的集合,求有多少种情况使得S1 ∩ S2 ∩ ... ∩ Sk = ∅.
      首先想到N个元素的集合的子集个数有2^n个,因此要选出K个子集则有(2^n)^k=2^(n*k)种情况。
      若这k个子集中都至少包含x1,则S(x1)=2^[(n-1)*k],至少包含x1,x2,则S(x1&x2)=2^(n-2*k)……
      所以根据容斥原理,可以得到这个式子:
      S=2^(n*k)+C(n,1)*2^[(n-1)*k]+C(n,2)*2^[(n-2)*k]+……+C(n,n)*2^(0)
      所以S=(2^k-1)^n

3003120 2012-08-06 09:44:00 Accepted 3556 C++ 0 188 hanker
View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 
 6 using namespace std;
 7 typedef long long ll;
 8 
 9 const ll MOD=1000000007;
10 
11 ll Pow(int n,int m)
12 {
13     ll a=1,b=n;
14     while(m)
15     {
16         if(m%2)
17         {
18             a*=b;
19             a%=MOD;
20         }
21         b*=b;
22         b%=MOD;
23         m/=2;
24     }
25     return a;
26 }
27 
28 int main()
29 {
30     int n,k;
31     ll sum;
32     while(scanf("%d%d",&n,&k)!=EOF)
33     {
34         sum=Pow(2,k)-1;
35         sum%=MOD;
36         sum=Pow((int)sum,n);
37         printf("%lld\n",sum);
38     }
39     return 0;
40 }

 

3.hdu 3929


   题意:给出以(1+x)为基底的多项式,要求最终多项式中x的奇系数个数
   首先思考 对于(1+x)^n中奇系数个数=2^(n二进制表示中1的个数),但如何确定x^k的系数是否为奇数
   若(n&k)==k则x^k的系数为奇数,反之为偶数。故对于(1+x)^n+(1+x)^m要求得最终多项式中x的奇系数
   的个数 由于对于x^k来说,要满足(n&k)==k,(m&k)==k,所以(n&m&k)==k。所以满足条件的所有个数=2^(n&m二进制表示中1的个数)
   于是采用容斥原理
   其中注意一个小知识点:整数运算 x&(-x),当x为0时结果为0;x为奇数时,结果为1;x为偶数时,结果为x中2的最大次方的因子。

   待续....

 6426713    2012-08-03 15:18:15    Accepted    3929    5078MS    488K    1111 B    G++    hanker

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 
 6 using namespace std;
 7 typedef long long ll;
 8 
 9 ll a[30],ans;
10 
11 int get(ll x)
12 {
13    return x==0?0:get(x-(x&-x))+1;
14 }
15 
16 ll dfs(ll s,int k,int n,ll num)
17 {
18     ans+=(1ll<<get(s))*num;
19     for(int i=k+1;i<n;i++)
20         dfs(s&a[i],i,n,-2*num);
21 }
22 int main()
23 {
24     int n,m;
25     while(scanf("%d",&n)!=EOF)
26     {
27         for(int i=1;i<=n;i++)
28         {
29             ans=0;
30             scanf("%d",&m);
31             for(int j=0;j<m;j++)
32                 scanf("%lld",&a[j]);
33             for(int j=0;j<m;j++)
34                 dfs(a[j],j,m,1);
35             printf("Case #%d: %lld\n",i,ans);
36         }
37     }
38     return 0;
39 }
posted @ 2012-07-30 20:39  hankers  阅读(301)  评论(0编辑  收藏  举报