UVALive 7040 Color 容斥,组合数递推,线形逆元,基础数论

来源:互联网 发布:网络歌手阿刚歌曲 编辑:程序博客网 时间:2024/04/30 05:39

题目链接:这里
题意:首先有T组数据,每组数据有 3 个数 n, m, k,分别代表一共有 n 个方格,m种颜色,而我们要 恰好(注意是恰好) 使用 k 中颜色对这些方格进行涂色,并且保证的是每两个相邻的方格的颜色必须是不一样的。
解法:m种颜色选k种,所以有C(m,k),对于选出的k种颜色去给n个人涂色,对于第一个人有k种,第二个人有k-1种,第三个人k-1种。。。。。所以总共是k*(k-1)^(n-1)种。然而不要以为这样就好了,因为这样算并不能保证是一定用了k种颜色的,可能只是选了k种其中的2,3,4,,,,k-1种,所以要用容斥原理来去重,所以最后的式子就是C(m,k) × ( k × (k-1)^(n-1) + ∑((-1)^p × C(k, p) × p × (p-1)^(n-1) ) (2 <= p <= k-1)。然后窝们为了计算这个式子,必须线形预处理逆元和C(i, m)和C(j, k)其中 i <= m 和 j <= k。线形逆元不懂可以看这位大佬的
然后组合数递推的式子是怎么样的呢?
这里写图片描述

//LA 7040#include <bits/stdc++.h>using namespace std;const int mod = 1e9 + 7;const int maxn = 1e6 + 7;typedef long long LL;LL inv[maxn], cm[maxn], ck[maxn], n, m, k;LL powmod(LL a, LL n){    LL res = 1;    while(n){        if(n & 1) res = res * a % mod;        a = a * a % mod;        n >>= 1LL;    }    return res;}void init(){    inv[1] = 1;    for(long long i = 2; i <= 1000006; i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;}void pre_deal(){    cm[0] = ck[0] = 1;    for(long long 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(){    init();    int T, ks = 0; scanf("%d", &T); while(T--){        scanf("%lld%lld%lld", &n, &m, &k);        pre_deal();        int t = 1;        LL ans = 0;        for(long long i = k; i >= 1; i--){            ans = (ans + t * i * ck[i] % mod * powmod(i - 1, n - 1) % mod + mod) % mod;            t = -t;        }        ans = ans * cm[k] % mod;        printf("Case #%d: %lld\n", ++ks, ans);    }    return 0;}
0 0
原创粉丝点击