容斥原理专题
容斥原理适用于 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 |
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 |
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
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 }