二项式反演

 

hdu 1465 http://acm.hdu.edu.cn/showproblem.php?pid=1465

UVALive 7040 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5052

 

问:给你k种颜色,你必须用上所有颜色去涂满n个相邻的格子,并且要求相邻格子的颜色不同,求方案数。

 

我们设必须用 i 种颜色两两不相邻的涂格子的方案数为 b(i) ;

很明显: ,我们令 a(k)=k·(k-1)n-1 , 然后有.

如果你知道二项式反演的话,那么这个问题就已经解决了,因为.

 

是不是觉得二项式反演很厉害,下面我将给出它的证明。

  • 二项式反演公式:

      

 

  • 证明:

   

   

   然后让我们对进行分析:

    我们预热一下:

      有A,B,C,D,E,F,G 7个人,我们要先从中选出4个候选人,再从中选出3个作为master。

      那么我们可以很容易写出其方案数:

      但是我们用另一种邪恶的思路思考这个问题

         我们可以先内部选出3个master,在从剩下的4个人选出一个来:

      所以我们很容易的得到了这个等式:

 

    所以有

    然后你会发现剩下的部分就是 (1 - 1)n-j的项张开而已,所以:

    ,所以原式 = b(n) .

       所以:成立。

       

#include<bits/stdc++.h>
using namespace std;
const int M = 1e6 + 10 ;
int n , m , k ;
const int mod = 1e9 + 7 ;
int key ;
int f[M] , inv[M] , finv[M] ;

void table () {
        inv[1] = 1 ;
        for (int i = 2 ; i < M ; i ++) inv[i] = (mod - mod/i) * 1ll * inv[mod%i] % mod ;
        f[0] = finv[0] = 1 ;
        for (int i = 1 ; i < M ; i ++) {
                f[i] = 1ll*f[i-1] * i % mod ;
                finv[i] = 1ll*finv[i-1] * inv[i] % mod ;
        }
}

int qkpow (int a , int b) {
        int ret = 1 ;
        while (b) {
                if (b & 1) ret = 1ll*ret*a % mod ;
                b >>= 1 ;
                a = 1ll*a*a%mod ;
        }
        return ret ;
}

int comb (int n , int m) {
        if (m < 0 || n < m) return 0 ;
        return 1ll * f[n] * finv[m] % mod * finv[n-m] % mod ;
}

int solve (int x) {
        //cout << qkpow (-1 , k-x) << " " << comb (k,x) << " " << x << " " << qkpow (x-1 , n-1) << endl ; 
        return 1ll*qkpow(-1 , k-x) * comb (k , x) * x % mod * qkpow (x-1 , n-1) % mod ;
}

int main () {
        int T ;
        table () ;
        scanf ("%d" , &T ) ;
        for (int cas = 1 ; cas <= T ; cas ++) {
                scanf ("%d%d%d" , &n , &m , &k) ;
                printf ("Case #%d: " , cas ) ;
                key = 0 ;
                for (int i = 0 ; i <= k ; i ++) {
                        //puts ("heheh") ;
                        key = ((1ll*key + 1ll*solve (i) ) % mod + mod ) % mod ;
                }
                //puts ("======") ;
                int ans = 1 ;
                for (int i = 1 ; i <= k ; i ++) {
                        ans = 1ll*ans*(m-k+i)%mod ;
                }
                printf ("%d\n" , key*1ll*ans%mod*finv[k]%mod ) ;
        }
        return 0 ;
}

 

posted @ 2015-10-04 22:24  92度的苍蓝  阅读(1563)  评论(0编辑  收藏  举报
http://images.cnblogs.com/cnblogs_com/Running-Time/724426/o_b74124f376fc157f352acc88.jpg