首页 > 代码库 > 容斥原理学习
容斥原理学习
简单入门题目:
UVA10325 The lottery http://vjudge.net/vjudge/contest/view.action?cid=53767#problem/A
设A[I]表示 是其中i个数的倍数的个数
SUM=A[1]-A[2]+A[3]-A[4]。。。。。
ANS=N-SUM;
代码如下:
[cpp] view plaincopy
- #include <iostream>
- #include <cstring>
- #include <cstdio>
- #define LL long long
- using namespace std;
- LL a[15];
- LL gcd(LL a,LL b){
- if(b)
- return gcd(b,a%b);
- return a;
- }
- LL lcm(LL a,LL b){
- return a/gcd(a,b)*b;
- }
- int main()
- {
- int m;
- LL n,ans;
- while(cin>>n>>m){
- for(int i=0;i<m;i++)
- cin>>a[i];
- ans=0;
- for(int i=1;i<(1<<m);i++){
- LL l=1,flag=0;
- for(int j=0;j<m;j++){
- if((1<<j)&i){
- l=lcm(l,a[j]);
- if(l>n) break;
- flag++;
- }
- }
- if(flag&1)
- ans+=n/l;
- else
- ans-=n/l;
- }
- cout<<n-ans<<endl;
- }
- return 0;
- }
SPOJ Another Game With Numbers http://vjudge.net/vjudge/contest/view.action?cid=53767#problem/C
思路同上。
代码如下:
- #include <iostream>
- #include <cstring>
- #include <cstdio>
- #define LL long long
- using namespace std;
- LL a[15];
- LL gcd(LL a,LL b){
- if(b)
- return gcd(b,a%b);
- return a;
- }
- LL lcm(LL a,LL b){
- return a/gcd(a,b)*b;
- }
- int main()
- {
- int m;
- LL n,ans;
- while(cin>>n>>m){
- for(int i=0;i<m;i++)
- cin>>a[i];
- ans=0;
- for(int i=1;i<(1<<m);i++){
- LL l=1,flag=0;
- for(int j=0;j<m;j++){
- if((1<<j)&i){
- l=lcm(l,a[j]);
- if(l>n) break;
- flag++;
- }
- }
- if(flag&1)
- ans+=n/l;
- else
- ans-=n/l;
- }
- cout<<n-ans<<endl;
- }
- return 0;
- }
UVA 11806 Cheerleaders http://vjudge.net/vjudge/contest/view.action?cid=53767#problem/B
分析:
题目的意思就是 m*n个格子 在上面放k个 且第一行第一列最后一行最后一列都必须得放
反面考虑,考虑所有不符合条件的情况 ,一共有15种 ,然后用所有的情况减去即可;
代码如下:
- #include <iostream>
- #include <cstring>
- #include <cstdio>
- #define LL long long
- using namespace std;
- const int maxn = 410;
- const int mod = 1000007;
- int com[maxn][maxn];
- void init(){
- memset(com,0,sizeof(com));
- for(int i=0;i<maxn;i++)
- com[i][0]=1;
- for(int i=1;i<maxn;i++){
- for(int j=1;j<=i;j++)
- com[i][j]=(com[i-1][j-1]%mod+com[i-1][j]%mod)%mod;
- }
- }
- int n,m,k;
- int solve(){
- int ans=0;
- for(int i=1;i<(1<<4);i++){
- int num=0,r=n,c=m;
- if(i&1) r--,num++;
- if(i&2) r--,num++;
- if(i&4) c--,num++;
- if(i&8) c--,num++;
- if(num%2==0)
- ans=(ans+mod-com[r*c][k])%mod;
- else
- ans=(ans+com[r*c][k])%mod;
- }
- return (com[m*n][k]-ans+mod)%mod;
- }
- int main()
- {
- int t,cnt=1;
- init();
- cin>>t;
- while(t--){
- cin>>n>>m>>k;
- int ans=solve();
- printf("Case %d: %d\n",cnt++,ans);
- }
- return 0;
- }
POJ 3904 Sky Code :http://poj.org/problem?id=3904
分析:
反面考虑,求出取出四个数的所有情况中 GCD(A,B,C,D)!=1的情况;我们只需将每一个数都给素因子分解,然后看使用不重复的素因子所能组成的因子的个数,并统计组成该因子 的素因子的个数
例如 形成的的因子2 的个数 为a,形成因子3的个数为b,形成6的因子的个数为c 则cnt=com(a,4)+com(b,4)-com(c,4);
代码如下:
- #include<iostream>
- #include<cstdlib>
- #include<stdio.h>
- #include<memory.h>
- #define maxn 10005
- using namespace std;
- long long Count[maxn];
- long long num[maxn];
- long long p[maxn];
- long long prime[maxn];
- void init()
- {
- memset(p,0,sizeof(p));
- memset(num,0,sizeof(num));
- for(long long i=4;i<maxn;i++)
- p[i]=i*(i-1)*(i-2)*(i-3)/24;
- }
- void solve(long long n)
- {
- long long tol=0;
- for(long long i=2;i*i<=n;i++)
- {
- if(n%i==0)
- {
- prime[tol++]=i;
- }
- while(n%i==0)
- n/=i;
- }
- if(n!=1)
- prime[tol++]=n;
- for(long long i=1;i<(1<<tol);i++)
- {
- long long k=1;
- long long sum=0;
- for(long long j=0;j<tol;j++)
- {
- if(i&(1<<j))
- {
- k*=prime[j];
- sum++;
- }
- }
- Count[k]++;//记录当前因子的个数
- num[k]=sum;//记录当前因子是由多少个素因子组成
- }
- }
- int main()
- {
- init();
- long long n;
- long long m;
- while(scanf("%lld",&n)!=EOF)
- {
- memset(Count,0,sizeof(Count));
- for(long long i=0;i<n;i++)
- {
- scanf("%lld",&m);
- solve(m);
- }
- long long ans=0;
- for(long long i=0;i<maxn;i++)
- {
- if(Count[i])
- {
- if(num[i]%2)
- {
- ans+=p[Count[i]];
- }
- else
- ans-=p[Count[i]];
- }
- }
- cout<<p[n]-ans<<endl;
- }
- return 0;
- }
SPOJ 4168 Square-free integers
http://vjudge.net/vjudge/contest/view.action?cid=53767#problem/E
分析:
求1~n中有多少个数可以写成 x^2,我们可以把这样的数写成 p^(2*q),
1~n中这样的数的个数为n/(p*p);中间重复的数我们根据容斥原理可以去掉
系数为莫比乌斯函数值
代码如下:
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- using namespace std;
- typedef long long LL;
- const int maxn = 10000100;
- bool p[maxn];
- int mu[maxn],prime[maxn],cnt;
- //线性筛选求莫比乌斯反演
- void Init()
- {
- memset(p,0,sizeof(p));
- mu[1] = 1;
- cnt = 0;
- for(int i=2; i<maxn; i++){
- if(!p[i]){
- prime[cnt++] = i;
- mu[i] = -1;
- }
- for(int j=0; j<cnt&&i*prime[j]<maxn; j++){
- p[i*prime[j]] = 1;
- if(i%prime[j]) mu[i*prime[j]] = mu[prime[j]]*mu[i];
- else
- break;
- }
- }
- }
- int main()
- {
- Init();
- LL n,ans;
- int ca;
- scanf("%d",&ca);
- while(ca--){
- scanf("%lld",&n);
- ans=0;
- for(int i=1;i<=n/i;i++){
- if(mu[i])
- ans+=n/i/i*mu[i];
- }
- printf("%lld\n",ans);
- }
- return 0;
- }
容斥原理学习