容斥原理(三元容斥,四元容斥)


 

 

 

題意:    已知集合A,B,C,  輸出三集合的並集。

 

容斥原理(用圖解釋)

對於求三集合並集的公式:

  A∪B∪C=A+B+C - A∩B - A∩C - B∩C + A∩B∩C

  對於證明,我就簡單的敘述一下。

    因為求並集不能將兩集合的重復元素進行相加。而 A+B+C  沒有考慮重復元素,直接相加,顯然這是元素多加的情況,那要還原必須要減去多加的部分,對於上圖藍色部分只加了一次,紅色部分加了兩次,綠色的部分加了三次,那么我們只需要使他們全部只加一次就能得到正確答案。

所以我們要執行下操作     A+B+C -A∩B + A∩C + B∩C) ,但還不算完美,因為在這里綠色部分被減了三次,要使把綠色部分只減一次,那么要再加上一個綠色部分即可。

 

 

 

對於求四集合並集的公式:

A∪B∪C∪D=A+B+C+D - A∩B - B∩C  -  C∩A -  A∩D  -  B∩D  -  C∩D + A∩B∩C + A∩B∩D  + A∩C∩D  + B∩C∩D  -  A∩B∩C∩D    規律   集合數    奇加偶減

 

對於證明類似於上列三元並集證明。   

 

貼一題目(四元):

  

 
 
給出一個數N,求1至N中,有多少個數不是2 3 5 7的倍數。 例如N = 10,只有1不是2 3 5 7的倍數。
 
輸入
輸入1個數N(1 <= N <= 10^18)。
輸出
輸出不是2 3 5 7的倍數的數共有多少。
輸入樣例
10
輸出樣例
1

 

AC代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
#define Mem0(x) memset(x,0,sizeof(x))
#define Mem1(x) memset(x,-1,sizeof(x))
#define MemX(x) memset(x,0x3f,sizeof(x))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f;
const double pi=acos(-1.0);

ll n;
int main()
{
    ll ans=0;
    cin>>n;
    ans=n/2+n/3+n/5+n/7;
    ans=ans-n/6-n/10-n/14-n/15-n/21-n/35+n/30+n/42+n/105+n/70-n/210;
    cout<<n-ans<<endl;
    return 0;
}

 

再貼一題:  (提供鏈接,就不貼題目了)

https://ac.nowcoder.com/acm/contest/634/C    

 
 
 
解題:
 
  

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
#define Mem0(x) memset(x,0,sizeof(x))
#define Mem1(x) memset(x,-1,sizeof(x))
#define MemX(x) memset(x,0x3f,sizeof(x))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f;
const double pi=acos(-1.0);


ll l,r,k;
ll prime[500050];
bool check[1000010];
int cnt;
void prim()
{
    memset(check,false,sizeof(check));
    check[0]=check[1]=true;
    cnt=0;
    for (int i=2;i<500000;i++){
        if (!check[i])
            prime[cnt++]=i;
        for (int j=0;j<cnt&&i*prime[j]<1000000;j++){
            check[i*prime[j]]=true;
            if (i%prime[j]==0)
                break;
        }
    }
}
ll a[1000010];
int main()
{
    prim();
    cin>>l>>r>>k;
    ll p=0;//質因數的個數 
    ll tmp=k<<1;
/*    for (int i=0;;i++){
        if (tmp%prime[i]==0){
            a[p++]=prime[i];
            while (tmp%prime[i]==0){
                tmp/=prime[i];
            }
        }
        if (tmp<=1)
            break;
    }*/
    for(int i=2;i*i<=tmp;i++){
        if(!(tmp%i)){
            a[p++]=i;
            while(!(tmp%i))
                tmp/=i;
        }
    }
    if(tmp>1)
        a[p++]=tmp;
    
    ll sum=0;
    tmp=1<<p;//  存在p個質數,則有pow(2,p)種的組合數,
    for (int i=0;i<tmp;i++){
        ll t=1,s=0;   //t是質因數的公倍數,s則為選舉的質因數的個數 
        for (int j=0;j<p;j++){
            if (i&(1<<j)){
                s++;
                t*=a[j];
            }
        }
         if(r/t>(l+2*k-1)/t){  //容斥  奇加偶減 
            if(s%2)sum-=r/t-(l+2*k-1)/t;
            else sum+=r/t-(l+2*k-1)/t;
        }        
    }
    cout<<sum<<endl; 
    return 0;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM