• 二项式反演及其应用


    概念

    二项式反演为一种反演形式,常用于通过 “指定某若干个” 求 “恰好若干个” 的问题。

    注意:二项式反演虽然形式上和多步容斥极为相似,但它们并不等价,只是习惯上都称之为多步容斥。

    引入

    既然形式和多步容斥相似,我们就从多步容斥讲起。

    我们都知道:(|Acup B|=|A|+|B|-|Acap B|) ,这其实就是容斥原理。

    它的一般形式为:

    [|A_1cup A_2cup...cup A_n|=sumlimits_{1le ile n}|A_i|-sumlimits_{1le i<jle n}|A_icap A_j|+...+(-1)^{n-1} imes |A_1cap A_2cap ...cap A_n| ]

    证明:

    设某一元素被 (m) 个集合所包含,则其对左侧的贡献为 (1)
    对右侧的贡献为 (sumlimits_{i=1}^m(-1)^{i-1}{mchoose i}=-sumlimits_{i=1}^m(-1)^i{mchoose i}=1-sumlimits_{i=0}^m(-1)^i{mchoose i}=1-(1-1)^m=1)

    故左侧等于右侧 ,证毕。

    形式

    形式零

    沿用刚刚多步容斥的公式,记 (A_i^c) 表示 (A_i) 的补集,则将一般形式变形,可以得到:

    [|A_1^ccap A_2^ccap ...cap A_n^c|=|S|-sumlimits_{1le ile n}|A_i|+sumlimits_{1le i<jle n}|A_icap A_j|-...+(-1)^n imes |A_1cap A_2cap ...cap A_n| ]

    同时,由于补集的补集就是原集,因此又有:

    [|A_1cap A_2cap ...cap A_n|=|S|-sumlimits_{1le ile n}|A_i^c|+sumlimits_{1le i<jle n}|A_i^ccap A_j^c|-...+(-1)^n imes |A_1^ccap A_2^ccap ...cap A_n^c| ]

    考虑一种特殊情况:多个集合的交集大小只和集合的数目有关。

    (f(n)) 表示 (n) 个补集的交集大小,(g(n)) 表示 (n) 个原集的大小,则两个公式分别可以写成:

    [f(n)=sumlimits_{i=0}^n(-1)^i{nchoose i}g(i)\ g(n)=sumlimits_{i=0}^n(-1)^i{nchoose i}f(i) ]

    显然这两个公式是等价关系,更是相互推导的关系,于是我们得到了二项式反演的形式零:

    [f(n)=sumlimits_{i=0}^n(-1)^i{nchoose i}g(i)Leftrightarrow g(n)=sumlimits_{i=0}^n(-1)^i{nchoose i}f(i) ]

    形式一

    公式如下:

    [f(n)=sumlimits_{i=0}^n{nchoose i}g(i)Leftrightarrow g(n)=sumlimits_{i=0}^n(-1)^{n-i}{nchoose i}f(i) ]

    证明一

    在形式零中,令 (h(n)=(-1)^ng(n)) ,则形式零就变为了:

    [f(n)=sumlimits_{i=0}^n{nchoose i}h(i)Leftrightarrow frac{h(n)}{(-1)^n}=sumlimits_{i=0}^n(-1)^i{nchoose i}f(i) ]

    整理后就是形式一。

    证明二

    将右侧代入左侧,则:

    [egin{split} f(n)&=sumlimits_{i=0}^n{nchoose i}sumlimits_{j=0}^i(-1)^{i-j}{ichoose j}f(j)\&=sumlimits_{i=0}^nsumlimits_{j=0}^i(-1)^{i-j}{nchoose i}{ichoose j}f(j) end{split} ]

    考虑调换两个求和符号的顺序,即先枚举 (i) ,再枚举 (j) ,则又有:

    [f(n)=sumlimits_{j=0}^nf(j)sumlimits_{i=j}^n(-1)^{i-j}{nchoose i}{ichoose j} ]

    考虑 ({nchoose i}{ichoose j}) 的组合意义:从 (n) 个中选 (i) 个,再从 (i) 个中选 (j) 个。不妨反过来想,先从 (n) 个中选 (j) 个,再从剩下的 (n-j) 个中选出 (i-j) 个,即 ({nchoose j}{n-jchoose i-j})

    于是可以得到:

    [egin{split} f(n)&=sumlimits_{j=0}^n{nchoose j}f(j)sumlimits_{i=j}^n{n-jchoose i-j}(-1)^{i-j}\ &=sumlimits_{j=0}^n{nchoose j}f(j)sumlimits_{t=0}^{n-j}{n-jchoose t}(-1)^t1^{n-j-t}\ &=sumlimits_{j=0}^n{nchoose j}f(j)(1-1)^{n-j} end{split} ]

    (n-j eq 0) 时,显然 ((1-1)^{n-j}=0)
    (n-j=0) 时,出现 (0^0) 不能直接计算,需要使用组合形式求解,此时 ({n-jchoose t}(-1)^{t}=1)

    (sumlimits_{t=0}^{n-j}{n-jchoose t}(-1)^t=[j=n]) ,于是:

    [f(n)={nchoose n}f(n)=f(n) ]

    左右恒等,证毕。

    注:由于证明二并未用到 (i)(0) 开始这一性质,因此更通用的公式为:

    [f(n)=sumlimits_{i=m}^n{nchoose i}g(i)Leftrightarrow g(n)=sumlimits_{i=m}^n(-1)^{n-i}{nchoose i}f(i) ]

    形式二

    这个形式和形式一类似,是最常用的公式。公式如下:

    [f(n)=sumlimits_{i=n}^m{ichoose n}g(i)Leftrightarrow g(n)=sumlimits_{i=n}^m(-1)^{i-n}{ichoose n}f(i) ]

    证明

    将右侧代入左侧,则:

    [egin{split} f(n)&=sumlimits_{i=n}^m{ichoose n}sumlimits_{j=i}^m(-1)^{j-i}{jchoose i}f(j)\ &=sumlimits_{i=n}^msumlimits_{j=i}^m(-1)^{j-i}{ichoose n}{jchoose i}f(j)\ &=sumlimits_{j=n}^mf(j)sumlimits_{i=n}^j(-1)^{j-i}{ichoose n}{jchoose i}\ &=sumlimits_{j=n}^m{jchoose n}f(j)sumlimits_{i=n}^j{j-nchoose j-i}(-1)^{j-i}\ &=sumlimits_{j=n}^m{jchoose n}f(j)sumlimits_{t=0}^{j-n}{j-nchoose t}(-1)^{t}1^{j-n-t}\ &=sumlimits_{j=n}^m{jchoose n}f(j)(1-1)^{j-n}\ &=sumlimits_{j=n}^m{jchoose n}f(j)[j=n]\ &={nchoose n}f(n)\ &=f(n) end{split} ]

    左右恒等,证毕。

    组合意义

    (f(n)) 表示 “钦定选 (n) 个”,(g(n)) 表示 “恰好选 (n) 个”,则对于任意的 (ige n)(g(i))(f(n)) 中被计算了 (ichoose n) 次,故 (f(n)=sumlimits_{i=n}^m{ichoose n}g(i)) ,其中 (m) 是数目上界。

    注意:在定义中,(f(n)) 表示先钦定 (n) 个,再统计钦定情况如此的方案数,其中会包含重复的方案,因为一个方案可以有多种钦定情况。具体地,对于恰好选择 (i) 个,钦定情况数位 (ichoose n) ,故 g(i) 在 (f(i)) 中被计算了 (ichoose n) 次。切勿将 (f(n)) 来理解为普通的后缀和。

    例题

    [bzoj2839]集合计数

    题目大意

    一个有 (n) 个元素的集合有 (2^n) 个不同子集(包含空集),现在要在这 (2^n) 个集合中取出至少一个集合,使得它们的交集的元素个数为 (k) ,求取法的方案数模 (10^9+7)

    (1le nle 10^6)(0le kle n)

    题解

    对于稍有组合数学基础的人,通过直觉很容易列出式子 ({nchoose i}(2^{2^{n-i}}-1)) 。即钦定 (i) 个交集元素,则包含这 (i) 个的集合有 (2^{n-i}) 个;每个集合可选可不选,但不能都不选,由此可得此方案数。

    接下来考虑上式与所求的关系:设 (f(i)) 表示钦定交集元素为某 (i) 个的方案数, (g(i)) 表示交集元素恰好为 (i) 个的方案数,则 ({nchoose k}(2^{2^{n-k}-1})=f(k)=sumlimits_{i=k}^n{nchoose i}g(i))

    通过二项式反演求出 (g(k)=sumlimits_{i=k}^n(-1)^{i-k}{ichoose k}f(i)=sumlimits_{i=k}^n(-1)^{i-k}{ichoose k}{nchoose 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
    " , (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) imes (cnt(i)-(j-1)))

    (dp(i,j)=dp(i-1,j)+dp(i-1,j-1) imes(cnt(i)-(j-1)))

    (f(i)) 表示钦定 (i)(A>B) 的方案数,(g(i)) 表示恰好 (i)(A>B) 的方案数,则 ((n-m)! imes dp(n,m)=f(m)=sumlimits_{i=m}^n{ichoose m}g(i))

    (g(m)=sumlimits_{i=m}^n(-1)^{i-m}{ichoose m}f(i)=sumlimits_{i=m}^n(-1)^{i-m}{ichoose m}(n-i)! imes 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
    " , 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-1choose a_i-1}) , 于是 ({nchoose t}prodlimits_{i=1}^m{n-t+a_i-1choose a_i-1}=f(t)=sumlimits_{i=t}^n{ichoose t}g(i))

    (g(t)=sumlimits_{i=t}^n(-1)^{i-t}{ichoose t}f(i)=sumlimits_{i=t}^n(-1)^{i-t}{ichoose t}{nchoose i}prodlimits_{j=1}^m{n-i+a_j-1choose a_j-1})

    最终的答案 (g(0)=sumlimits_{i=0}^n(-1)^i{nchoose i}prodlimits_{j=1}^m{n-i+a_j-1choose 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
    " , ans);
        return 0;
    }
    
  • 相关阅读:
    771. Jewels and Stones
    706. Design HashMap
    811. Subdomain Visit Count
    733. Flood Fill
    117. Populating Next Right Pointers in Each Node II
    250. Count Univalue Subtrees
    94. Binary Tree Inorder Traversal
    116. Populating Next Right Pointers in Each Node
    285. Inorder Successor in BST
    292. Nim Game Java Solutin
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/11407185.html
Copyright © 2020-2023  润新知