题目大意:直线上给n个物品染色,一共有m种颜色,求恰好用了k种颜色的染色方案数
解析:
首先选出k种颜色,选法C(m,k), 那么对于不超过k种颜色的涂色方案数为 f[k]= k*(k-1)^(n-1) 表示第一个有k种选择,后面的全都是k-1种选择 。 这里面包含了 k=1,2,3,......k的全部情况
ps: 最开始智障了,觉得f[k]-f[k-1]不显然就是答案了吗。。nc啊。。。
ps: 明显在选出了k种颜色的条件下,f[k]为不超过k种颜色的所有方案,那么就是要减去不超过k-1种颜色的所有方案,但是k种颜色里选k-1种又有一个 C(k,k-1),所以应该减去的是C(k,k-1)*f(k-1),而显然这样f(k-2)那些又会被减掉了需要加回来,加加减减就是一眼容斥原理啦
已选出k种颜色后,那么对于最后涂了不超过i种(2<=i<=k-1)颜色的方案数为g[i]= C(k,i)* i*(i-1)^(n-1)
所以对于选出来的k种颜色下,我们要染恰好k个颜色的合法方案数应该为ans[k]= f[k] -g[k-1] +g[k-2]-g[k-3]........
最后答案为 c(m,k)*ans[k]
由于算g[i]之和时要用到一个c[k]【i】
然后最后算答案要求一个C【m】【k】
p又特别大 (1e9+7)
lucas预处理p的那种肯定不行,直接算的单case的又太慢了
注意到C几几的第二项比较小,只有k,
则可以用O(K)去算出所有的组合数,公式:c(n,k+1)=c(n,k)*(n-k)/(1+k)
那么就预处理1到k的逆元
整体复杂度O(K)
using namespace std;
typedef long long ll;
const ll p =1000000007;
ll fast_m(ll a,ll b)
{
ll x,ans=1;
x=a;
while(b)
{
if (b&1)
ans=(ans*x)%p;
x=(x*x)%p;
b/=2;
}
return ans;
}
ll n,m,k;
ll inv[1000000+5];
void Init_inv()
{
for(int i = 1; i < 1e6+50; i++)
inv[i] = fast_m(i, p - 2);
}
ll c[1000000+50];
void cal(ll n)
{
c[0] = 1;
for(int i = 1; i <= k; i++)
c[i] = ((c[i - 1] * (n - i + 1) % p) * inv[i] % p) % p;
}
ll f(ll k)
{
return k*fast_m(k-1,n-1)%p;
}
int main()
{
Init_inv();
int t;
cin>>t;
int cnt=1;
while(t--)
{
scanf("%lld%lld%lld",&n,&m,&k);
cal(k);
ll ansk=f(k);
for (int i=2; i<=k-1; i++)
{
if ((k-i)%2) ansk=(ansk-c[i]*f(i)%p )%p;
else ansk=(ansk+ (c[i]*f(i)) %p) %p;
}
cal(m);
printf("Case #%d: %lld\n",cnt++, (c[k]*ansk%p+p)%p);
}
return 0;
}