NC13884 Paint Box(排列组合&&容斥原理)

链接:https://ac.nowcoder.com/acm/problem/13884
来源:牛客网
 

题目描述

We have n empty boxes, so let’s recolor those boxes with m colors.

The boxes are put in a line. It is not allowed to color any adjacent boxes with the same color. Boxes i and i+1 are said to be adjacent for every i,1≤i≤n. 

And we also want the total number of different colors of the n boxes being exactly k. 

Two ways are considered different if and only if there is at least one box being colored with different colors.

输入描述:

 

The first line of the input contains integer T(1≤T≤100) -the number of the test cases 

For each case: there will be one line, which contains three integers n,m,k(1≤n,m≤109 1≤k≤106, k≤n,m).

输出描述:

For each test case, you need print an integer means the number of ways of different coloring methods modulo 109+7.

示例1

输入

复制2 3 2 2 3 2 1

2
3 2 2
3 2 1

输出

复制2 0

2
0

说明

In the first example we have two ways:
121
212
where 1 and 2 are two different color.
In the second example we can't do that.

思路:

首先从m中颜色中选k个来作为涂色的当前颜色方案,然后用i个颜色为box涂色的方案为:f(i)=i*(i-1)^(n-1),即:第一次可从i种颜色中选一个来涂,但是因为相邻的box颜色不能相同,所以,后面n-1个box都是从除去前一种的i-1中中选一个来涂色。

这题的关键在于恰好k种颜色所以利用容斥原理来求:

即:最终方案数=至多用k种颜色方案数-至多用k-1种颜色方案数+至多用k-2种颜色方案数-至多用k-3种颜色方案数+...+(-)至多用1种颜色方案数。

最后,利用乘法原理将选出k种颜色的方法数与用k种颜色上色的方法数相乘,得到的答案就是:
C(m,k)* (C(k,k)* f(k)-C(k,k-1)* f(k-1)+C(k,k-2)* f(k-2)-...+(-1)^{k}(−1)k* f(1))

其中因为m较大,所以不能预处理出阶乘和阶乘的逆元,而k较小,且C(m,k)只需求一次,所以可直接计算得到其结果;而C(k,i)则可以由C(k,i-1)推出:

即C(k,i) = k*(k-1)*(k-i+1)/i! = (k/1)*((k-1)/2)*..*(k-i+1/i)   

而从后往前求可表示为:i/(k-i+1)*(i-1)/(k-i+1)*..*1/(k-i+1)  i从k到1, 

除以(k-i+1)可以转化为乘(k-i+1)的逆元: 即qpow((k-i+1),mod-2)

依次可以求出C(k,1) = i/(k-i+1) ,C(k,2) = i/(k-i+1)*((i-1)/(k-i+1)  (即:C(k,2) = C(k,1)*(i-1)/(k-i+1)  )  .....

代码:

#include <iostream>
#include <algorithm>
#include <cstring>

#define int long long

using namespace std;

const int mod=1e9+7;

int t,n,m,k;

int qpow(int a,int b)
{
    int res=1%mod;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

int C(int n,int m)
{
    if(n<m) return 0;
    if(n-m<m) m=n-m;
    int a=1,b=1;
    for(int i=0;i<m;i++){
        a=(a*(n-i))%mod,b=(b*(i+1))%mod;
    }
    return a*qpow(b,mod-2)%mod;
}

signed main()
{
    cin>>t;
    while(t--){
        cin>>n>>m>>k;
        int sum=0,c=1;
        for(int i=k;i>=1;i--){
            sum=(sum+i*qpow(i-1,n-1)%mod*c%mod+mod)%mod;
            c=-1*c*i%mod*qpow(k-i+1,mod-2)%mod;
        }
        int res=sum*C(m,k)%mod;
        cout<<res<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值