容斥(多個集合並問題)


學習博客:https://blog.csdn.net/m0_37286282/article/details/78869512

對容斥原理的描述

容斥原理是一種重要的組合數學方法,可以讓你求解任意大小的集合,或者計算復合事件的概率。

描述

       容斥原理可以描述如下:

         要計算幾個集合並集的大小,我們要先將所有單個集合的大小計算出來,然后減去所有兩個集合相交的部分,再加回所有三個集合相交的部分,再減去所有四個集合相交的部分,依此類推,一直計算到所有集合相交的部分。

關於集合的原理公式

      上述描述的公式形式可以表示如下:
       

        

 

      它可以寫得更簡潔一些,我們將B作為所有Ai的集合,那么容斥原理就變成了:

        

         這個公式是由 De Moivre (Abraham de Moivre)提出的。

關於維恩圖的原理

       用維恩圖來表示集合A、B和C:

       

         那么的面積就是集合A、B、C各自面積之和減去 ,  的面積,再加上的面積。

         由此,我們也可以解決n個集合求並的問題。

關於概率論的原理

       設事件 代表發生某些事件的概率(即發生其中至少一個事件的概率),則:

  

         這個公式也可以用B代表Ai的集合:

容斥原理的證明

       我們要證明下面的等式:

       

         其中B代表全部Ai的集合

         我們需要證明在Ai集合中的任意元素,都由右邊的算式被正好加上了一次(注意如果是不在Ai集合中的元素,是不會出現在右邊的算式中的)。

         假設有一任意元素在k個Ai集合中(k>=1),我們來驗證這個元素正好被加了一次:

         當size(C)=1時,元素x被加了k次。

         當size(C)=2時,元素x被減了C(2,k)次,因為在k個集合中選擇2個,其中都包含x。

         當size(C)=3時,元素x被加了C(3,k)次。

         ……

         當size(C)=k時,元素x被加/減了C(k,k)次,符號由sign(-1)^(k-1)決定。

         當size(C)>k時,元素x不被考慮。

         然后我們來計算所有組合數的和。

         

         由二項式定理,我們可以將它變成:

    

 

         我們把x取為1,這時表示1-T(其中T為x被加的總次數),所以,證明完畢。

對於實際問題的應用

       容斥原理的理論需要通過例子才能很好的理解。

         首先,我們用三個簡單的例子來闡釋這個理論。然后會討論一些復雜問題,試看如何用容斥原理來解決它們。

         其中的“尋找路徑數”是一個特殊的例子,它反映了容斥問題有時可以在多項式級復雜度內解決,不一定需要指數級。

一個簡單的排列問題

       由0到9的數字組成排列,要求第一個數大於1,最后一個數小於8,一共有多少種排列?

         我們可以來計算它的逆問題,即第一個元素<=1或者最后一個元素>=8的情況。

         我們設第一個元素<=1時有X組排列,最后一個元素>=8時有Y組排列。那么通過容斥原理來解決就可以寫成:

       

         經過簡單的組合運算,我們得到了結果:

         

         然后被總的排列數10!減,就是最終的答案了。

(0,1,2)序列問題

       長度為n的由數字0,1,2組成的序列,要求每個數字至少出現1次,這樣的序列有多少種?

         同樣的,我們轉向它的逆問題。也就是不出現這些數字的序列 不出現其中某些數字的序列。

         我們定義Ai(i=0…2)表示不出現數字i的序列數,那么由容斥原理,我們得到該逆問題的結果為:

           可以發現每個Ai的值都為2^n(因為這些序列中只能包含兩種數字)。而所有的兩兩組合都為1(它們只包含1種數字)。最后,三個集合的交集為0。(因為它不包含數字,所以不存在)

        要記得我們解決的是它的逆問題,所以要用總數減掉,得到最終結果:

         

方程整數解問題

       給出一個方程:

       

         其中

        求這個方程的整數解有多少組。

        我們先不去理會xi<=8的條件,來考慮所有正整數解的情況。這個很容易用組合數來求解,我們要把20個元素分成6組,也就是添加5塊“夾板”,然后在25個位置中找5塊“夾板”的位置。

         

         然后通過容斥原理來討論它的逆問題,也就是x>=9時的解。

         我們定義Ak為xk>=9並且其他xi>=0時的集合,同樣我們用上面的添加“夾板”法來計算Ak的大小,因為有9個位置已經被xk所利用了,所以:

         

         然后計算兩個這樣的集合Ak、Ap的交集:

         

         因為所有x的和不能超過20,所以三個或三個以上這樣的集合時是不能同時出現的,它們的交集都為0。最后我們用總數剪掉用容斥原理所求逆問題的答案,就得到了最終結果:

         

求指定區間內與n互素的數的個數:

       給出整數n和r。求區間[1;r]中與n互素的數的個數。

         去解決它的逆問題,求不與n互素的數的個數。

         考慮n的所有素因子pi(i=1…k)

         在[1;r]中有多少數能被pi整除呢?它就是:

       

         然而,如果我們單純將所有結果相加,會得到錯誤答案。有些數可能被統計多次(被好幾個素因子整除)。所以,我們要運用容斥原理來解決。

         我們可以用2^k的算法求出所有的pi組合,然后計算每種組合的pi乘積,通過容斥原理來對結果進行加減處理。

         關於此問題的最終實現:

#include<iostream>
#include<vector>
using namespace std;
void solve(int n,int r)
{
    vector<int> p;
    for(int i=2;i*i<=n;i++)//求n的所有質因子
    {
        if(n%i==0)
        {
            p.push_back(i);
            while(n%i==0)
            {
                n/=i;
            }
        }
    }
    if(n>1) p.push_back(n);

    //容斥
//    cout<<"*&"<<endl;
    int ans=0;
    int len=p.size();
//    cout<<len<<" "<<endl;
    for(int i=1;i<(1<<len);i++)//二進制枚舉
    {
//        cout<<"*"<<endl;
        int mul=1,cnt=0;
        for(int j=0;j<len;j++)
        {
//            cout<<"*"<<endl;
            if(i&(1<<j))
            {
                cnt++;
                mul*=p[j];
            }
        }
        int sum=r/mul;
        if(cnt&1) ans+=sum;
        else ans-=sum;
    }
    cout<<r-ans<<endl;
}
int main()
{
    int n,r;
    cin>>n>>r;
    solve(n,r);
    return 0;
}

 


免責聲明!

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



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