【容斥原理+逆元+组合数+费马小定理+快速幂】UVALive - 7040 Color

Problem Description

t组数据,每组给定n,m,k。有n个格子,m种颜色,要求把每个格子涂上颜色且正好适用k种颜色且相邻的格子颜色不同,求一共有多少种方案,结果对1e9+7取余。

思路:

参考大佬博客传送门

首先可以将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<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 1e9+7;
const int maxn = 1e6+5;
ll inv[maxn], cm[maxn], ck[maxn];
ll n, m, k;
ll Pow(ll x, ll n)//快速幂
{
    ll sum = 1;
    while(n)
    {
        if(n&1) sum = sum*x % mod;
        x = x*x % mod;
        n >>= 1;
    }
    return sum;
}
void get_inv()//求1-maxn的逆元
{
    for(ll i = 1; i < maxn; i++)
        inv[i] = Pow(i, mod-2);
}
void get_mk()//求C(m, k), C(k, i) i属于1-k
{
    cm[0] = 1; ck[0] = 1;
    for(ll i = 1; i <= k; i++)
    {
        cm[i] = (cm[i-1] * ((m-i+1)%mod * inv[i]%mod)%mod)%mod;
        ck[i] = (ck[i-1] * ((k-i+1)%mod * inv[i]%mod)%mod)%mod;
    }
}
int main()
{
    int T, Case = 1;
    scanf("%d", &T);
    get_inv();//求逆元
    while(T--)
    {
        scanf("%lld %lld %lld", &n, &m, &k);
        get_mk();
        ll ans = 0, p = 1;
        for(ll i = k; i >= 1; i--)//容斥定理
        {
            ans = (ans+(p*(ck[i]*(i*Pow(i-1, n-1)%mod)%mod)%mod) + mod)%mod;
            p = -p;
        }
        ans = ans * cm[k] % mod;
        printf("Case #%d: %lld\n", Case++, ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值