CF GYM 100548 容斥原理+快速幂+逆元

来源:互联网 发布:藏族歌曲下载软件 编辑:程序博客网 时间:2024/05/20 05:56

Recently, Mr. Big recieved n owers from his fans. He wants to recolor those owers with m colors. The
owers are put in a line. It is not allowed to color any adjacent owers with the same color. Flowers i and
i + 1 are said to be adjacent for every i, 1  i < n. Mr. Big also wants the total number of different
colors of the n owers being exactly k.
Two ways are considered different if and only if there is at least one ower being colored with different
colors.
Input
The rst line of the input gives the number of test cases, T. T test cases follow. T is about 300 and in
most cases k is relatively small.
For each test case, there will be one line, which contains three integers n, m, k (1  n;m  109,
1  k  106, k  n;m).
Output
For each test case, output one line containing `Case #x: y', where x is the test case number (starting
from 1) and y is the number of ways of different coloring methods modulo 109 + 7.
Sample Input
2
3 2 2
3 2 1
Sample Output
Case #1: 2
Case #2: 0

题意:

n个格子排成一行,有m种颜色,问用恰好k种颜色进行染色,使得相邻格子颜色不同的方案数。 
k106n,m109

题解:

首先,我们可以从m个颜色中取出k个,即 C k m 。

接着容易想到

k ∗ ( k − 1 ) n − 1

, 这个是使用不超过k种颜色的所有方案。

但我们要求的是恰好使用k种颜色。

假设选出的k种颜色标号为1,2,3,…, k,那么记 A i 为 不使用

颜色i的方案数,求的就是

| S | − | A 1 ⋃ A 2 ⋃ ⋯ ⋃ A n |

也就是反过来考虑,我们不考虑用了哪些颜色,我们考虑哪些颜色没用!减去所有有没使用颜色的方案的并集,剩下的方案就是使用了所有k种颜色的方案。上式中的 | S| 即 k ∗ ( k − 1 ) n − 1 ,后者就可以用容斥原理来求了。注意到我们只是给颜色标了个号,所以后面每一项的应为 C i k ∗ ( k − i ) ∗ ( k − i − 1 ) n − 1 的形式,即选出i个不使用的颜色,用剩余颜色去涂的方案数。

这样,我们就可以得到答案公式

C(m,k) × ( k × (k-1)^(n-1) + ∑((-1)^p × C(k, p) × p × (p-1)^(n-1) ) (2 <= p <= k-1);

这下子,我们就开始理解这个公式:

我们定义ans[x]代表从k个颜色中选出[2, k]种颜色的方案数

从2个颜色开始,ans[2]就代表从k个颜色中选2个颜色的方案数

那么3个颜色呢?最开始我们就得到C(k, 3) × 3 × (2)^(n-1),这有什么不对呢?对于(1,2,3)和(2, 3, 4)俩个集合来说,(2,3)这2种颜色多算了一次。

由上一次可得,对于C(k, 3) × 3 × (2)^(n-1)来说,它比我们想要得到的ans[3]多了1个ans[2],

那么对于ans[4]来说,C(k, 4) × 4 × (3)^(n-1)比ans[4]多了什么呢?

没错,还是多了1个ans[3]。

/*************************************************************************    > File Name: uvalive_7040.cpp    > Author: james47    > Mail:    > Created Time: Sat Aug  8 08:47:40 2015 ************************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>using namespace std;const int mod = (int)1e9+7;int pow_mod(int a, int exp){    int ret = 1;    while(exp){        if (exp&1) ret = (long long)ret * a % mod;        a = (long long)a * a % mod;        exp >>= 1;    }    return ret;}int c[(int)1e6+100];int inv[(int)1e6+100];int cal(int n, int m, int k){    if (k == 1 && n == 1) return m;    if (k == 1 && n > 1) return 0;    int ret = 1, lim = min(k, m - k);    for (int i = 1; i <= lim; i++) ret = (long long)ret * (m-i+1) % mod * inv[i] % mod;    c[0] = 1;    for (int i = 1; i <= k; i++) c[i] = (long long)c[i-1] * (k-i+1) % mod * inv[i] % mod;    int tmp, tot = (long long)k * pow_mod(k-1, n-1) % mod;    for (int i = 1; i+1 < k; i++){        tmp = (long long)c[i] * (k-i) % mod * pow_mod(k-i-1, n-1) % mod;        if (i&1){            tot = tot - tmp;            if (tot < 0) tot += mod;        }        else{            tot = tot + tmp;            if (tot >= mod) tot -= mod;        }    }    ret = (long long)ret * tot % mod;    return ret;}void init(){    inv[1] = 1;    for (int i = 2; i <= 1000000; i++){        inv[i] = (long long)(mod - mod/i) * inv[mod % i] % mod;    }}int T, n, m, k;int main(){    init();    scanf("%d", &T); int cas = 0;    while(T--){        scanf("%d %d %d", &n, &m, &k);        printf("Case #%d: %d\n", ++cas, cal(n, m, k));    }    return 0;}

 

0 0
原创粉丝点击