二項式反演及其應用


概念

二項式反演為一種反演形式,常用於通過 “指定某若干個” 求 “恰好若干個” 的問題。

注意:二項式反演雖然形式上和多步容斥極為相似,但它們並不等價,只是習慣上都稱之為多步容斥。

引入

既然形式和多步容斥相似,我們就從多步容斥講起。

我們都知道:\(|A\cup B|=|A|+|B|-|A\cap B|\) ,這其實就是容斥原理。

它的一般形式為:

\[|A_1\cup A_2\cup...\cup A_n|=\sum\limits_{1\le i\le n}|A_i|-\sum\limits_{1\le i<j\le n}|A_i\cap A_j|+...+(-1)^{n-1}\times |A_1\cap A_2\cap ...\cap A_n| \]

證明:

設某一元素被 \(m\) 個集合所包含,則其對左側的貢獻為 \(1\)
對右側的貢獻為 \(\sum\limits_{i=1}^m(-1)^{i-1}{m\choose i}=-\sum\limits_{i=1}^m(-1)^i{m\choose i}=1-\sum\limits_{i=0}^m(-1)^i{m\choose i}=1-(1-1)^m=1\)

故左側等於右側 ,證畢。

形式

形式零

沿用剛剛多步容斥的公式,記 \(A_i^c\) 表示 \(A_i\) 的補集,則將一般形式變形,可以得到:

\[|A_1^c\cap A_2^c\cap ...\cap A_n^c|=|S|-\sum\limits_{1\le i\le n}|A_i|+\sum\limits_{1\le i<j\le n}|A_i\cap A_j|-...+(-1)^n\times |A_1\cap A_2\cap ...\cap A_n| \]

同時,由於補集的補集就是原集,因此又有:

\[|A_1\cap A_2\cap ...\cap A_n|=|S|-\sum\limits_{1\le i\le n}|A_i^c|+\sum\limits_{1\le i<j\le n}|A_i^c\cap A_j^c|-...+(-1)^n\times |A_1^c\cap A_2^c\cap ...\cap A_n^c| \]

考慮一種特殊情況:多個集合的交集大小只和集合的數目有關。

\(f(n)\) 表示 \(n\) 個補集的交集大小,\(g(n)\) 表示 \(n\) 個原集的大小,則兩個公式分別可以寫成:

\[f(n)=\sum\limits_{i=0}^n(-1)^i{n\choose i}g(i)\\ g(n)=\sum\limits_{i=0}^n(-1)^i{n\choose i}f(i) \]

顯然這兩個公式是等價關系,更是相互推導的關系,於是我們得到了二項式反演的形式零:

\[f(n)=\sum\limits_{i=0}^n(-1)^i{n\choose i}g(i)\Leftrightarrow g(n)=\sum\limits_{i=0}^n(-1)^i{n\choose i}f(i) \]

形式一

公式如下:

\[f(n)=\sum\limits_{i=0}^n{n\choose i}g(i)\Leftrightarrow g(n)=\sum\limits_{i=0}^n(-1)^{n-i}{n\choose i}f(i) \]

證明一

在形式零中,令 \(h(n)=(-1)^ng(n)\) ,則形式零就變為了:

\[f(n)=\sum\limits_{i=0}^n{n\choose i}h(i)\Leftrightarrow \frac{h(n)}{(-1)^n}=\sum\limits_{i=0}^n(-1)^i{n\choose i}f(i) \]

整理后就是形式一。

證明二

將右側代入左側,則:

\[\begin{split} f(n)&=\sum\limits_{i=0}^n{n\choose i}\sum\limits_{j=0}^i(-1)^{i-j}{i\choose j}f(j)\\&=\sum\limits_{i=0}^n\sum\limits_{j=0}^i(-1)^{i-j}{n\choose i}{i\choose j}f(j) \end{split} \]

考慮調換兩個求和符號的順序,即先枚舉 \(i\) ,再枚舉 \(j\) ,則又有:

\[f(n)=\sum\limits_{j=0}^nf(j)\sum\limits_{i=j}^n(-1)^{i-j}{n\choose i}{i\choose j} \]

考慮 \({n\choose i}{i\choose j}\) 的組合意義:從 \(n\) 個中選 \(i\) 個,再從 \(i\) 個中選 \(j\) 個。不妨反過來想,先從 \(n\) 個中選 \(j\) 個,再從剩下的 \(n-j\) 個中選出 \(i-j\) 個,即 \({n\choose j}{n-j\choose i-j}\)

於是可以得到:

\[\begin{split} f(n)&=\sum\limits_{j=0}^n{n\choose j}f(j)\sum\limits_{i=j}^n{n-j\choose i-j}(-1)^{i-j}\\ &=\sum\limits_{j=0}^n{n\choose j}f(j)\sum\limits_{t=0}^{n-j}{n-j\choose t}(-1)^t1^{n-j-t}\\ &=\sum\limits_{j=0}^n{n\choose j}f(j)(1-1)^{n-j} \end{split} \]

\(n-j\neq 0\) 時,顯然 \((1-1)^{n-j}=0\)
\(n-j=0\) 時,出現 \(0^0\) 不能直接計算,需要使用組合形式求解,此時 \({n-j\choose t}(-1)^{t}=1\)

\(\sum\limits_{t=0}^{n-j}{n-j\choose t}(-1)^t=[j=n]\) ,於是:

\[f(n)={n\choose n}f(n)=f(n) \]

左右恆等,證畢。

注:由於證明二並未用到 \(i\)\(0\) 開始這一性質,因此更通用的公式為:

\[f(n)=\sum\limits_{i=m}^n{n\choose i}g(i)\Leftrightarrow g(n)=\sum\limits_{i=m}^n(-1)^{n-i}{n\choose i}f(i) \]

形式二

這個形式和形式一類似,是最常用的公式。公式如下:

\[f(n)=\sum\limits_{i=n}^m{i\choose n}g(i)\Leftrightarrow g(n)=\sum\limits_{i=n}^m(-1)^{i-n}{i\choose n}f(i) \]

證明

將右側代入左側,則:

\[\begin{split} f(n)&=\sum\limits_{i=n}^m{i\choose n}\sum\limits_{j=i}^m(-1)^{j-i}{j\choose i}f(j)\\ &=\sum\limits_{i=n}^m\sum\limits_{j=i}^m(-1)^{j-i}{i\choose n}{j\choose i}f(j)\\ &=\sum\limits_{j=n}^mf(j)\sum\limits_{i=n}^j(-1)^{j-i}{i\choose n}{j\choose i}\\ &=\sum\limits_{j=n}^m{j\choose n}f(j)\sum\limits_{i=n}^j{j-n\choose j-i}(-1)^{j-i}\\ &=\sum\limits_{j=n}^m{j\choose n}f(j)\sum\limits_{t=0}^{j-n}{j-n\choose t}(-1)^{t}1^{j-n-t}\\ &=\sum\limits_{j=n}^m{j\choose n}f(j)(1-1)^{j-n}\\ &=\sum\limits_{j=n}^m{j\choose n}f(j)[j=n]\\ &={n\choose n}f(n)\\ &=f(n) \end{split} \]

左右恆等,證畢。

組合意義

\(f(n)\) 表示 “欽定選 \(n\) 個”,\(g(n)\) 表示 “恰好選 \(n\) 個”,則對於任意的 \(i\ge n\)\(g(i)\)\(f(n)\) 中被計算了 \(i\choose n\) 次,故 \(f(n)=\sum\limits_{i=n}^m{i\choose n}g(i)\) ,其中 \(m\) 是數目上界。

注意:在定義中,\(f(n)\) 表示先欽定 \(n\) 個,再統計欽定情況如此的方案數,其中會包含重復的方案,因為一個方案可以有多種欽定情況。具體地,對於恰好選擇 \(i\) 個,欽定情況數位 \(i\choose n\) ,故 g(i) 在 \(f(i)\) 中被計算了 \(i\choose n\) 次。切勿將 \(f(n)\) 來理解為普通的后綴和。

例題

[bzoj2839]集合計數

題目大意

一個有 \(n\) 個元素的集合有 \(2^n\) 個不同子集(包含空集),現在要在這 \(2^n\) 個集合中取出至少一個集合,使得它們的交集的元素個數為 \(k\) ,求取法的方案數模 \(10^9+7\)

\(1\le n\le 10^6\)\(0\le k\le n\)

題解

對於稍有組合數學基礎的人,通過直覺很容易列出式子 \({n\choose i}(2^{2^{n-i}}-1)\) 。即欽定 \(i\) 個交集元素,則包含這 \(i\) 個的集合有 \(2^{n-i}\) 個;每個集合可選可不選,但不能都不選,由此可得此方案數。

接下來考慮上式與所求的關系:設 \(f(i)\) 表示欽定交集元素為某 \(i\) 個的方案數, \(g(i)\) 表示交集元素恰好為 \(i\) 個的方案數,則 \({n\choose k}(2^{2^{n-k}-1})=f(k)=\sum\limits_{i=k}^n{n\choose i}g(i)\)

通過二項式反演求出 \(g(k)=\sum\limits_{i=k}^n(-1)^{i-k}{i\choose k}f(i)=\sum\limits_{i=k}^n(-1)^{i-k}{i\choose k}{n\choose i}(2^{2^{n-i}}-1)\)

使用一些預處理手段,時間復雜度 \(O(n)\)

代碼

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
ll fac[1000010] , ine[1000010] , fi[1000010] , pow[1000010];
ll choose(ll n , ll m)
{
    return fac[n] * fi[m] % mod * fi[n - m] % mod;
}
int main()
{
    int n , k , i;
    ll ans = 0;
    scanf("%d%d" , &n , &k);
    fac[0] = fac[1] = ine[1] = fi[0] = fi[1] = 1;
    for(i = 2 ; i <= n ; i ++ )
    {
        fac[i] = fac[i - 1] * i % mod;
        ine[i] = (mod - mod / i) * ine[mod % i] % mod;
        fi[i] = fi[i - 1] * ine[i] % mod;
    }
    pow[0] = 2;
    for(i = 1 ; i <= n ; i ++ ) pow[i] = pow[i - 1] * pow[i - 1] % mod;
    for(i = k ; i <= n ; i ++ )
    {
        if((i & 1) == (k & 1)) ans = (ans + choose(n , i) * choose(i , k) % mod * (pow[n - i] - 1 + mod)) % mod;
        else ans = (ans - choose(n , i) * choose(i , k) % mod * (pow[n - i] - 1 + mod) % mod + mod) % mod; 
    }
    printf("%lld\n" , (ans + mod) % mod);
    return 0;
}

[bzoj3622]已經沒有什么好害怕的了

題目大意

給出兩個長度均為 \(n\) 的序列 \(A\)\(B\) ,保證這 \(2n\) 個數互不相同。現要將 \(A\) 序列中的數與 \(B\) 序列中的數兩兩配對,求 “ \(A>B\) 的對數比 \(A<B\) 的對數恰好多 \(k\) ” 的配對方案數模 \(10^9+9\)

題解

顯然當 \(n-k\) 為奇數時必然無解;當 \(n-k\) 為偶數時,\(A>B\) 的對數恰好為 \(\frac{n+k}2\) ,記 \(m=\frac{n+k}2\)

由於 “恰好” 這一限制不容易處理,考慮將其轉化為 “欽定” 限制,進而通過二項式反演來處理。

先將 \(A\)\(B\) 從小到大排序,設 \(dp(i,j)\) 表示考慮了 \(A\) 的前 \(i\) 個數,欽定了 \(j\)\(A>B\) 的方案數。

討論 \(A_i\) 的配對情況:若不配對,則方案數為 \(dp(i-1,j)\) ;若配對,記 \(B\) 中比 \(A_i\) 小的數的個數為 \(cnt(i)\) ,則方案數為 \(dp(i-1,j-1)\times (cnt(i)-(j-1))\)

\(dp(i,j)=dp(i-1,j)+dp(i-1,j-1)\times(cnt(i)-(j-1))\)

\(f(i)\) 表示欽定 \(i\)\(A>B\) 的方案數,\(g(i)\) 表示恰好 \(i\)\(A>B\) 的方案數,則 \((n-m)!\times dp(n,m)=f(m)=\sum\limits_{i=m}^n{i\choose m}g(i)\)

\(g(m)=\sum\limits_{i=m}^n(-1)^{i-m}{i\choose m}f(i)=\sum\limits_{i=m}^n(-1)^{i-m}{i\choose m}(n-i)!\times dp(n,i)\)

時間復雜度 \(O(n^2)\)

代碼

#include <cstdio>
#include <algorithm>
#define N 2010
#define mod 1000000009
using namespace std;
typedef long long ll;
int a[N] , b[N];
ll c[N][N] , fac[N] , f[N][N];
int main()
{
    int n , k , i , j , p = 0;
    ll ans = 0;
    scanf("%d%d" , &n , &k);
    if((n ^ k) & 1)
    {
        puts("0");
        return 0;
    }
    k = (n + k) >> 1;
    for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]);
    for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &b[i]);
    sort(a + 1 , a + n + 1) , sort(b + 1 , b + n + 1);
    for(i = 0 ; i <= n ; i ++ ) f[i][0] = 1;
    for(i = 1 ; i <= n ; i ++ )
    {
        while(p < n && b[p + 1] < a[i]) p ++ ;
        for(j = 1 ; j <= n ; j ++ )
            f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * (p - j + 1)) % mod;
    }
    c[0][0] = fac[0] = 1;
    for(i = 1 ; i <= n ; i ++ )
    {
        c[i][0] = 1 , fac[i] = fac[i - 1] * i % mod;;
        for(j = 1 ; j <= i ; j ++ )
            c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
    }
    for(i = k ; i <= n ; i ++ )
    {
        if((i ^ k) & 1) ans = (ans - c[i][k] * f[n][i] % mod * fac[n - i] % mod + mod) % mod;
        else ans = (ans + c[i][k] * f[n][i] % mod * fac[n - i]) % mod;
    }
    printf("%lld\n" , ans);
    return 0;
}

[bzoj4710]分特產

題目大意

\(n\) 個人和 \(m\) 種物品,第 \(i\) 種物品有 \(a_i\) 個,同種物品之間沒有區別。現在要將這些物品分給這些人,使得每個人至少分到一個物品,求方案數模 \(10^9+7\)

題解

對於多種物品的情況,“每個人至少分到一個物品” 是一個非常棘手的條件,考慮將其轉化為 “恰好 \(0\) 個人沒有分到物品” ,並用二項式反演來解決。

\(f(i)\) 表示欽定 \(i\) 個人沒有分到物品的方案數,\(g(i)\) 表示恰好 \(i\) 個人沒有分到物品的方案數,則在 \(f(t)\) 中,對於第 \(i\) 種物品,分配時相當於 \(a_i\) 個物品分給 \(n-t\) 個人,方案數為 \({n-t+a_i-1\choose a_i-1}\) , 於是 \({n\choose t}\prod\limits_{i=1}^m{n-t+a_i-1\choose a_i-1}=f(t)=\sum\limits_{i=t}^n{i\choose t}g(i)\)

\(g(t)=\sum\limits_{i=t}^n(-1)^{i-t}{i\choose t}f(i)=\sum\limits_{i=t}^n(-1)^{i-t}{i\choose t}{n\choose i}\prod\limits_{j=1}^m{n-i+a_j-1\choose a_j-1}\)

最終的答案 \(g(0)=\sum\limits_{i=0}^n(-1)^i{n\choose i}\prod\limits_{j=1}^m{n-i+a_j-1\choose a_j-1}\)

時間復雜度 \(O(n^2+nm)\)

代碼

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 2010
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
ll c[N][N];
int w[N];
int main()
{
    int n , m , i , j;
    ll ans = 0 , tmp;
    for(i = 0 ; i <= 2000 ; i ++ )
    {
        c[i][0] = 1;
        for(j = 1 ; j <= i ; j ++ )
            c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
    }
    scanf("%d%d" , &n , &m);
    for(i = 1 ; i <= m ; i ++ ) scanf("%d" , &w[i]);
    for(i = 0 ; i < n ; i ++ )
    {
        tmp = c[n][i];
        for(j = 1 ; j <= m ; j ++ )
            tmp = tmp * c[w[j] + n - i - 1][w[j]] % mod;
        if(i & 1) ans = (ans - tmp + mod) % mod;
        else ans = (ans + tmp) % mod;
    }
    printf("%lld\n" , ans);
    return 0;
}


免責聲明!

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



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