题意:t组数据,每组给定n,m,k。有n个格子,m种颜色,要求把每个格子涂上颜色且正好适用k种颜色且相邻的格子颜色不同,求一共有多少种方案,结果对1e9+7取余。
二项式反演(重点)
参考:http://www.cnblogs.com/linyujun/p/5210650.html
设g(i)表示正好有i个颜色涂格子
那么全部的C(k, i)*g(i)加起来正好就是所有涂格子情况,总共k(k-1)^(n-1)种情况
k(k-1)^(n-1) = Σ C(k, i)*g(i) (i从0到n)
那么f(k) = k(k-1)^(n-1),所以f(x) = x(x-1)^(n-1)
那么我们要求g(k)
根据公式:g(k) = Σ (-1)^(k-i) * C(k, i) * f(i) (i从0到n)
题解:
首先可以将m 与后面的讨论分离。从m 种颜色中取出k 种颜色涂色,取色部分有C(m, k) 种情况;
然后通过尝试可以发现,第一个有k种选择,第二个因不能与第一个相同,只有(k-1) 种选择,第三个也只需与第二个不同,也有(k-1) 种选择。总的情况数为k ×(k-1)^(n-1)。但这仅保证了相邻颜色不同,总颜色数不超过k种,并没有保证恰好出现k种颜色;
接着就是一个容斥问题,上述计算方法中包含了只含有2、3、…、(k-1)种颜色的情况,需要通过容斥原理去除。假设出现p (2 <= p <= k-1)种颜色,从k种颜色中选取p种进行涂色,方案数为C(k,p) × p × (p-1)^(n-1);
综上,最后的总方案数为C(m,k) × ( k × (k-1)^(n-1) + ∑((-1)^p × C(k, p) × p × (p-1)^(n-1) ) (2 <= p <= k-1);
最后,需要注意1 ≤ n, m ≤10^9,在进行指数运算时,需要使用快速幂。对于组合数,只需要计算C(m,k)和C(k,p) (1 <= p <= k),可以采用递推法,即C[x,i] = C[x, i-1] * (n-i+1) / i,因为要取模,所以需要用到i的逆元。
注意在总方案数的公式中p是大于等于2的,但是要特别注意当n=1的时候的情况,此时k只能为1,ans=m,如果k不为1,ans=0。
#include
#define ull unsigned long long
#define ll long long
const int mod =1000000007;
using namespace std;
const int maxn=1e6+5;
int a[maxn];
int t;
ll n,m,k;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
else
{
ll q=exgcd(b,a%b,y,x);
y=y-x*(a/b);
return q;
}
}
ll niyuan(ll a,ll n)//逆元
{
ll x,y;
ll d=exgcd(a,n,x,y);
if(d==1)
return (x%n+n)%n;
else
return -1;
}
ll qmod(ll n,ll m,ll mod)//快速幂
{
ll ans=1;
while(m)
{
if(m&1)ans=(ans*n)%mod;
n=(n*n)%mod;
m>>=1;
}
return ans%mod;
}
ll cm[maxn],ck[maxn],inv[maxn];
void get_inv()//打表
{
for(int i=1;i0]=1,ck[0]=1;
for(int i=1;i<=k;i++)
{
cm[i]=((cm[i-1]%mod*(m-i+1)%mod)*inv[i])%mod;
ck[i]=((ck[i-1]%mod*(k-i+1)%mod)*inv[i])%mod;
}
}
int main()
{
get_inv();
scanf("%d",&t);
int ca=1;
while(t--)
{
scanf("%lld%lld%lld",&n,&m,&k);
init();
ll ret;
ll ans=0;
for(ll i=k; i>=1; i--)
{
if((k-i)%2)
ret=-1;
else
ret=1;
ans=(ans+ret*i%mod*ck[i]%mod*qmod(i-1,n-1,mod)%mod+mod)%mod;
//二项式反演
}
ans=(ans*cm[k])%mod;
printf("Case #%d: %lld\n",ca++,ans);
}
}