容斥原理基本概念与例题

来源:互联网 发布:无线接入网络关键技术 编辑:程序博客网 时间:2024/05/22 16:57

注:本文参考自cyh大佬。

概念

  • |A ∪B| = |A| + |B| − |A ∩ B|
  • |A∪B ∪ C| = |A| + |B| + |C| − |A ∩ B| − |A ∩ C| − |B ∩ C| + |A ∩ B ∩ C|

这里写图片描述

例题

hdoj-2588-GCD

  • 题意:给定不超过 100 组正整数 N 和 M(2 ≤ N ≤ 1000000000, 1 ≤
    M ≤ N),求有多少个正整数 X 满足 1 ≤ X ≤ N 且gcd(X,N) ≥ M.
  • 标准的解法用欧拉函数。
这里采用容斥原理,以 N=10000 ,M=72 为例,先求出 10000 的因子为 80,100,125, 这些因子恰好大于 72 并且互相不能整除。再使用容斥 10000/80+10000/100+10000/125-10000/lcm(80,100)-10000/lcm(80,125)-10000/lcm(100,125)+10000/lcm(80,100,125) =125+100+80-25-5-20+5=260cnt=30   1   280 100 1251(2^0)     802(2^1)     1003(2^0|2^1) 80,1004          1255          80,1256          100,1257          80,100,125
#include<vector>#include<cstdio>#include<algorithm>using namespace std;int N,M;int gcd(int a,int b){    return b==0?a:gcd(b,a%b);}int lcm(int a,int b){    return a/gcd(a,b)*b;}int solve(){    if(M==1) return N;    int i,j,cnt;    vector<int>g;    for(i=1;i*i<=N;i++){        if(N%i==0){            if(i>=M) g.push_back(i);            if(N/i>=M) g.push_back(N/i);        }    }    sort(g.begin(),g.end());    int sz=g.size();    cnt=1;    for(i=1;i<sz;i++){        for(j=0;j<i;j++){            if(g[i]%g[j]==0)                break;        }        if(i==j) g[cnt++]=g[i];    }    int sum=0;    for(int num=1;num<(1<<cnt);num++){        int mult=1,ones=0;        for(i=0;i<cnt;i++){            if(num & (1<<i)){                ones++;                mult=lcm(mult,g[i]);            }        }        if(ones%2) sum+=N/mult;        else sum-=N/mult;    }    return sum;}int main(){    int T;    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&N,&M);        printf("%d\n",solve());    }    return 0;}

gdut-1174-我是好人4

  • 题意:给定 n 个数,问你 1000000000(含 1e9)以内有多少个正整数不是这 n
    个数中任意一个的倍数。
#include<vector>#include<cstdio>#include<algorithm>using namespace std;typedef long long LL;#define LIM 1e9int a[55];int n;LL gcd(LL x,LL y){    return y?gcd(y,x%y):x;}LL lcm(LL x,LL y){    return x/gcd(x,y)*y;}int dfs(int pos,int num,LL mult){    int ans=0;    if(mult>LIM)        return 0;    if(mult>LIM) return 0;    if(num&1)        ans-=LIM/mult;    else        ans+=LIM/mult;    for(int i=pos+1;i<n;i++){        ans+=dfs(i,num+1,lcm(a[i],mult));    }    return ans;}int main(){    int T,i,j;    scanf("%d",&T);    while(T--){        scanf("%d",&n);        for(i=0;i<n;i++)            scanf("%d",&a[i]);        sort(a,a+n);        int cnt=1;        for(i=1;i<n;i++){        //去掉重复数(如果a[i]/a[j],那么可以把大的a[i]去掉)            for(j=0;j<cnt;j++){                if(a[i]%a[j]==0) break;            }            if(j==cnt) a[cnt++]=a[i];        }        n=cnt;        printf("%d\n",dfs(-1,0,1LL));    }    return 0;}