容斥原理

来源:互联网 发布:js判断是否获取到元素 编辑:程序博客网 时间:2024/06/05 03:51

一、容斥原理

  在计数时,要保证无一重复,无一遗漏。为了使重叠部分不被重复计算,在不考虑重叠的情况下,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。

  1.容斥原理1——两个集合的容斥原理

  如果被计数的事物有A、B两类,那么,先把A、B两个集合的元素个数相加,发现既是A类又是B类的部分重复计算了一次,所以要减去。如图所示:

  公式:A∪B=A+B-A∩B

总数=两个圆内的-重合部分的

2.容斥原理2——三个集合的容斥原理

  如果被计数的事物有A、B、C三类,那么,将A、B、C三个集合的元素个数相加后发现两两重叠的部分重复计算了1次,三个集合公共部分被重复计算了2次。

  如图所示,灰色部分A∩B-A∩B∩C、B∩C-A∩B∩C、C∩A-A∩B∩C都被重复计算了1次,黑色部分A∩B∩C被重复计算了2次,因此总数A∪B∪C=A+B+C-(A∩B-A∩B∩C)-(B∩C-A∩B∩C)-(C∩A-A∩B∩C)-2A∩B∩C=A+B+C-A∩B-B∩C-C∩A+A∩B∩C。即得到:

  公式:A∪B∪C=A+B+C-A∩B-B∩C-C∩A+A∩B∩C

总数=三个圆内的-重合两次的+重合三次的

上面只是简单介绍一下容斥原理,那么代码如何写呢
就可以看看一篇讲的比较好的博客,里面有具体的实现方法还包括欧拉函数和抽屉原理的讲解。
点击打开博客http://blog.csdn.net/qq_36368339/article/details/70217849

附上两个实际的例子:
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1284

我只是单纯的应用到了这个原理,代码写得有点直接。

正确代码:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;typedef long long LL;const LL maxn=1e18;int main(){    LL n;    while(~scanf("%lld",&n))    {        LL a,b,c,d,ab,ac,ad,bc,bd,cd,abc,acd,abd,bcd,abcd;//15        a=n/2;        b=n/3;        c=n/5;        d=n/7;        ab=n/6;        ac=n/10;        ad=n/14;        bc=n/15;        bd=n/21;        cd=n/35;        abc=n/30;        acd=n/70;        abd=n/42;        bcd=n/105;        abcd=n/210;        abc=abc-abcd;//仅包括同时能被2,3,5整除的数,下面得到的结果依次类推        abd=abd-abcd;        acd=acd-abcd;        bcd=bcd-abcd;        ab=ab-(abc+abd+abcd);        ac=ac-(abc+acd+abcd);        ad=ad-(abd+acd+abcd);        bc=bc-(abc+bcd+abcd);        bd=bd-(abd+bcd+abcd);        cd=cd-(acd+bcd+abcd);        a=a-(ab+ac+ad+abc+abd+acd+abcd);        b=b-(ab+bc+bd+abc+abd+bcd+abcd);        c=c-(ac+bc+cd+abc+acd+bcd+abcd);        d=d-(ad+bd+cd+abd+acd+bcd+abcd);        LL ans;        ans=n-(a+b+c+d+ab+ac+ad+bc+bd+cd+abc+abd+acd+bcd+abcd);//此时的各部分之间没有重叠        printf("%lld\n",ans);    }    return 0;}

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6106
容斥原理

正确代码:

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;int main(){    int t;    scanf("%d",&t);    while(t--)    {        int n,maxn=-1;        scanf("%d",&n);        for(int i=0;i<n;i++)        {            int a,b,c,d,e,f,g,ab,bc,ac,abc;            scanf("%d%d%d%d%d%d%d",&a,&b,&c,&d,&e,&f,&g);            //接下来的操作将各部分之间重复的排除            d-=g;//ab            e-=g;//bc            f-=g;//ac            a=a-(d+f+g);            b=b-(d+e+g);            c=c-(e+f+g);            if(a<0||b<0||c<0||d<0||e<0||f<0||g<0)//判断数据是否有误                continue;            int s=a+b+c+d+e+f+g;            if(s>maxn)               maxn=s;        }        printf("%d\n",maxn);    }    return 0;}