数论 二项式反演

反演公式

f(n)=\sum_{r=1}^{n}C_{n,r} * g(r)

g(n)=\sum_{r=1}^{n}d_{n,r} * f(r)

c和d是两个跟n和r有关的函数

根据用法不同,c和d是不同的

一般数学家会先随便弄c函数

然后经过复杂的计算和证明,得到d函数

然后公式就可以套用了

 

二项式反演公式 (划重点)

f(n)=\sum_{i=0}^{n}\binom{n}{i}g(i)

g(n)=\sum_{i=0}^{n}(-1)^{n-i}\binom{n}{i}f(i)

^{C_{n}^{m}}=\binom{n}{m}

 

UVALive 7040 传送门

 

题意:

给n盆花涂色,从m种颜色中选取k种颜色涂,保证正好用上k种颜色,你必须用上这k种颜色去涂满n个相邻的花,并且要求相邻花的颜色不同,求方案数。(1 ≤ n, m ≤ 1e9 , 1 ≤ k ≤ 1e6 , k ≤ n, m)

思路:

首先,用k种颜色涂花,假如不考虑全部用上,那么总的方案数是多少

第一盆花有k种颜色选择,之后的花因为不能跟前一盆花的颜色相同,所以有k-1种选择

于是总方案数为k*(k-1)^(n-1)

我们设必须用 i 种颜色两两不相邻的涂花的方案数为 g(i)

那么

k*(k-1)^{n-1} = \sum C(k, i)*g(i)  (i从1到k)

令f(k) = k*(k-1)^(n-1),那么f(x) = x*(x-1)^(n-1)

二项式反演公式出现了

所以 g(k)=\sum (-1)^{k-i} * C(k, i) * f(i)  (i从1到k)

 

最终的答案就是C(m, k) * g(k)

(这里m有1e9,C(m, k)直接用for循环算,直接for循环从m*(m-1)*...*(m-k+1)再乘k的阶乘的逆元)

#include
#include
#include
#include
#include
#define ll long long
using namespace std;
const int N = 1000000 + 5;
const int MOD = (int)1e9 + 7;
int F[N], Finv[N], inv[N];//F是阶乘,Finv是逆元的阶乘 
void init(){
    inv[1] = 1;
    for(int i = 2; i < N; i ++){
        inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
    }
    F[0] = Finv[0] = 1;
    for(int i = 1; i < N; i ++){
        F[i] = F[i-1] * 1ll * i % MOD;
        Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD;
    }
}
int comb(int n, int m){//comb(n, m)就是C(n, m) 从n中选择m个 
    if(m < 0 || m > n) return 0;
    return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;
}
ll power(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1)
		ans=ans*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return ans%MOD;
}
int main(){
    init();
    int n,m,k;
    int t;
    cin>>t;
    int cas=1;
    while(t--)
	{
    	cin>>n>>m>>k;
    	int flag=(k&1)?1:-1;
    	ll ans=0;
		for(int i=1;i<=k;i++)
    	{
    		ans=ans+flag*comb(k,i)%MOD*(i*power(i-1,n-1)%MOD);
    		ans%=MOD;
    		flag=-flag;
    	}
    	ll temp=Finv[k];
    	for(int i=1;i<=k;i++)
    	{
    		temp=(temp*(m-k+i))%MOD;
    	}
    	ans=ans*temp%MOD;
    	printf("Case #%d: %lld\n",cas++,(ans+MOD)%MOD);
    }
    return 0;
}

 

你可能感兴趣的:(数论)