容斥原理

来源:互联网 发布:大数据相关技术介绍 编辑:程序博客网 时间:2024/06/05 04:10

容斥原理——这种思想很重要

由于前几天没有善待自己的身体,导致我昨天不得不去医院检查一下身体(︶^︶),结果医生说我是精神上的压力造成的,这个就让我很懵逼了,我觉得是我之前饮食不规律加上熬夜,然后又剧烈运动造成的(︶^︶),算了,今天吃了药,感觉好了一点,又来给小伙伴们分享一些小算法了,不过在这还是想提醒大家,身体真的很重要哦(^_^)∠※,好的,下面我们切入正题,来看看这个困扰了我一天的容斥定理是个什么鬼

容斥原理的定义:
上中学学集合的时候我们都知道这样两个公式:
A∪B = A+B – A∩B (∩:重合的部分)
A∪B∪C = A+B+C – A∩B – B∩C – C∩A +A∩B∩C

这两个公式帮助我们解决了很多以我们当时的逻辑能力很难想开的问题(下面是一些小的例子):

例1: 一次期末考试,某班有15人数学得满分,有12人语文得满分,并且有4人语、数都是满分,那么这个班至少有一门得满分的同学有多少人?
分析:
依题意,被计数的事物有语、数得满分两类,“数学得满分”称为“A类元素”,“语文得满分”称为“B类元素”,“语、数都是满分”称为“既是A类又是B类的元素”,“至少有一门得满分的同学”称为“A类和B类元素个数”的总和。
答案:
15+12-4=23

例2:电视台向100人调查前一天收看电视的情况,有62人看过2频道,34人看过8频道,其中11人两个频道都看过。两个频道都没看过的有多少人?
100-(62+34-11)=15

例3:(小学奥数题)某校六⑴班有学生45人,每人在暑假里都参加体育训练队,其中参加足球队的有25人,参加排球队的有22人,参加游泳队的有24人,足球、排球都参加的有12人,足球、游泳都参加的有9人,排球、游泳都参加的有8人,问:三项都参加的有多少人?
分析:
参加足球队的人数25人为A类元素,参加排球队人数22人为B类元素,参加游泳队的人数24人为C类元素,既是A类又是B类的为足球排球都参加的12人,既是B类又C类的为足球游泳都参加的9人,既是C类又是A类的为排球游泳都参加的8人,三项都参加的是A类B类C类的总和设为X。注意:这个题说的每人都参加了体育训练队,所以这个班的总人数即为A类B类和C类的总和。
答案:
25+22+24-12-9-8+X=45 ,解得X=3

例4:(高中题)在1到1000的自然数中,能被3或5整除的数共有多少个?不能被3或5整除的数共有多少个?
分析:
显然,这是一个重复计数问题(当然,如果不怕麻烦你可以分别去数3的倍数,5的倍数)。我们可以把“能被3或5整除的数”分别看成A类元素和B类元素,能“同时被3或5整除的数(15的倍数)”就是被重复计算的数,即“既是A类又是B类的元素”。求的是“A类或B类元素个数”。我们还不能直接计算,必须先求出所需条件。1000÷3=333……1,能被3整除的数有333个(想一想,这是为什么?)同理,可以求出其他的条件。

例5:(高中题)分母是1001的最简分数一共有多少个?
分析:这一题实际上就是找分子中不能与1001进行约分的数。由于1001=7×11×13,所以就是找不能被7,11,13整除的数。
解答:
1~1001中,有7的倍数1001/7 = 143 (个);有11的倍数1001/11 = 91 (个),有13的倍数1001/13 = 77 (个);有7*11=77;77是11的倍数1001/77 = 13 (个),有7*13=91;91是13的倍数;1001/91 = 11 (个),有11*13=143;143是13的倍数1001/143 = 7 (个).有1001的倍数1个。
由容斥原理知:在1~1001中,能被7或11或13整除的数有(143+91+77)-(13+11+7)+1=281(个),从而不能被7、11或13整除的数有1001-281=720(个).也就是说,分母为1001的最简分数有720个。

上面的两个公式其实是容斥原理在要求所并集合很少情况下的公式,那如果所并集合特别多呢?下面让我们慢慢揭开容斥原理这层神秘的面纱吧(⊙o⊙)

容斥原理是一种重要的组合数学方法,可以让你求解任意大小的集合,或者计算复合事件的概率。

容斥原理可以描述如下:
要计算几个集合并集的大小,我们要先将所有单个集合的大小计算出来,然后减去所有两个集合相交的部分,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分,依此类推,一直计算到所有集合相交的部分。
上述描述的公式形式可以表示如下:

这里写图片描述

它可以写得更简洁一些,我们将B作为所有Ai的集合,那么容斥原理就变成了:
这里写图片描述

这个公式是由 De Moivre (Abraham de Moivre)提出的。
我们来用维恩图来表示集合A、B和C:
这里写图片描述

那么最后的面积就是集合A、B、C各自面积之和减去A与B相交的面积,A与C相交的面积,B与C相交的面积,再加上A、B、C相交的面积。由此,我们也可以解决n个集合求并的问题。

容斥原理的证明:
这里写图片描述

下面是一些简单的应用例子:
例1:一个简单的排列问题
由0到9的数字组成排列,要求第一个数大于1,最后一个数小于8,一共有多少种排列?
我们可以来计算它的逆问题,即第一个元素<=1或者最后一个元素>=8的情况。
我们设第一个元素<=1时有X组排列,最后一个元素>=8时有Y组排列。那么通过容斥原理来解决就能够用到公式:
A∪B = A+B – A∩B (∩:重合的部分)
经过简单的组合运算,我们得到了结果:
这里写图片描述
然后被总的排列数10!减,就是最终的答案了。

例2:方程整数解问题

字可能有点小哈

下面我就以代码的形式给大家展示一下容斥原理在编程中如何使用的:

注意:在容斥原理中排列组合公式会被经常遇到:
这里写图片描述

例:求指定区间内与n互素的数的个数

Problem description:

Given a number N, you are asked to count the number of integers between A and B inclusive which are relatively prime to N. Two integers are said to be co-prime or relatively prime if they have no common positive divisors other than 1 or, equivalently, if their greatest common divisor is 1. The number 1 is relatively prime to every integer.

Input

The first line on input contains T (0 < T <= 100) the number of test cases, each of the next T lines contains three integers A, B, N where (1 <= A <= B <= 10 15) and (1 <=N <= 10 9).

Output

For each test case, print the number of integers between A and B inclusive which are relatively prime to N. Follow the output format below.

Sample Input

2
1 10 2
3 15 5

Sample Output

Case #1: 5
Case #2: 10

Hint
In the first test case, the five integers in range [1,10] which are relatively prime to 2 are {1,3,5,7,9}.

这道题的意思是指我先给你一个数N,然后再给你一个范围[A,B],然后让你找出该区间内所有和N互质的数的个数(不知道什么叫互质的同学,请点击该链接:http://blog.csdn.net/sand8o8time/article/details/76781541),要求先输入多少组测试数据,然后再分别输入A,B,N,输出要求是输出成Case #1: (结果)的形式(๑• . •๑)

举个例子 [1,10] 区间中与 6 互素的个数,应该是 10−(10/2+10/3) 但是这样多减去了 他们的最小公倍数 6 的情况,所以在加上 10/6 也就是:
10−(10/2+10/3−10/6)=3
这就用到了容斥原理,知道了这个,题目也就可以做了,先进行素因子分解,然后二进制枚举子集,再进行容斥原理(奇加偶减)。

看了我之前的博客,或者有一定的数论基础的大佬可能会发现如果所给的范围是1~n,那么就是欧拉函数了`(^﹏^)′,好的废话不多说,下面我们来看AC代码:

#include<iostream>using namespace std;typedef long long LL;const int Max = 1000010;int primeFactor[Max];int primeNum;LL A,B,N; void ergFac(int n){    primeNum = 0;    for(int i = 2;i*i<=n;i++){//只需要算到前根号n项即可,之前在欧拉函数中已经解释过,具体的请参看我的欧拉函数代码中的注释          if(n%i==0){             //这里的找质因子的处理方法不是很好,之前的欧拉函数模板中有更好的处理方法             primeFactor[primeNum++]=i;                  while(n%i==0)       //这个循环的作用是把n中所有的质因子都除掉,比如12就有2个2的质因子和1个3质因子                 n/=i;        }    }    if(n>1) primeFactor[primeNum++]=n;          //因为i是循环到根号n(起初的n),所以最后n(最后的n,n是一直在变化的)的值很有可能大于根号n(起初的n),并且为质数 }LL solve(LL y){    LL ans=0;                       //这个ans是用来记录1~y中所有和n不互质的数得的个数     for(LL i=1;i<(1<<primeNum);i++){        // i<(1<<primeNum)这个是不是感觉很神奇?我也想了好一会才想明白,这个相当于i<2^k,这个源于组合数学中的排列组合(在),Ck,1   Ck,2````Ck,k  ,没有Ck,0的原因是有k个因子,至少要选一个作为因子吧         LL ant=0;                   //ant是用来记录使用了多少个质因子         LL q=1;                     //q是用来记录使用的所有的质因子的乘积         for (LL j=0;j<primeNum;j++){        //j是用来做位运算的,第一层循环中我们发现 i<(1<<primeNum)             if(i&(1<<j)){           //看代码末尾                ant++;                q*=primeFactor[j];            }        }        if(ant&1)   ans+=y/q;   //这个是容斥原理中的容斥原理(奇加偶减)         else    ans-=y/q;       }    return ans;}int main(){    int t,flag=0;    cin>>t;     while(t--){        cin>>A>>B>>N;        ergFac(N);        cout<<"Case #"<<++flag<<": "<<B-A+1-solve(B)+solve(A-1)<<endl;    }       return 0;} //这个地方读者可以拿几组数据自己来调一调,理解的更快一点,这个是用来判断 ,主要是用来判断 从primeNum中选几个质因子相乘得q ,位运算很容易给大家整晕哈,但是用起来很是灵活 ,比如题目上的第二组测试数据,primeNum = 1,为2,那么i=1时能够判断i<(1<<primeNum),也就是说只有从1个数中取一个的情况,如果primeNum = 2,那么就有从2个中取1个,和2个中取2个的情况,一共有3中情况 (2个中取1个d的是两种) 
原创粉丝点击