UVA-7040(容斥+快速幂)

来源:互联网 发布:淘宝不包邮设置 编辑:程序博客网 时间:2024/06/03 17:13
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 first 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种颜色染这m朵花的方案数(相邻的花颜色不能相同)。

题解:先不考虑确切的(一般确切不会是直接能算的),先考虑满足相邻颜色不同的染色方案是这样的:第一朵花有k种颜色可以选,第二朵花有k-1种颜色可以选。所以方案数为i*pow(i,n-1)。接着考虑,这样的方案数是确切为k种颜色的吗?显然不是,这是小于等于k的方案数。于是yy到各种东西(例如莫比乌斯啦~~~~然而并没有用到),想了想,除去重复的,没有什么比容斥更合适的了。是的,容斥!

所以只需要枚举k到1,一加一减就可以得到答案了,然后再乘个C(m,k)就是最终答案了。

C(m,k)在最后跑一遍就可以了,C(k,i)则需要预先处理,不然再循环中跑就会导致k*k的复杂度而TLE。

然后此题最后一个要点就是逆元了。根据费马小定理,a的逆元为pow(a,mod-2)(mod为质数)。

AC。


代码:

#include <cstdio>#include <cstdlib>#include <iostream>#include <cstring>#include <string>#include <cstdlib>#include <algorithm>#include <queue>#include <vector>#include <set>#include <stack>#include <map>#include <cmath>#include <functional>#include <ctime>using namespace std;typedef long long ll;typedef pair<int, int> P;const int INF = 0x3f3f3f3f;const ll LINF = 0x3f3f3f3f3f3f3f3f;const ll mod = 1e9 + 7;const double PI = acos(-1.0);const double eps = 1e-10;const int maxn = 1e6+7;int n,m,T,k;ll inv[maxn];ll ck[maxn];ll qpow(ll a,ll b){    ll ans = 1;    while(b){        if(b&1) ans = ans*a%mod;        a = (a*a)%mod;        b>>=1;    }    return ans;}void init(){    for(int i = 1;i <= k;i++){        inv[i] = qpow(i,mod-2);    }    ck[0] = 1;    for(int i = 1;i <= k;i++){        ck[i] = (ck[i-1]*(k-i+1)%mod)*inv[i]%mod;    }}int main(){    scanf("%d",&T);    int I = 1;    while(T--){        scanf("%d %d",&n,&m);        scanf("%d",&k);        init();        ll ans = 0;        for(int i = k,sgn = 1;i >= 1;i--,sgn = -sgn){            ll res = (i*qpow(i-1,n-1)%mod)*ck[i]%mod;            ans = (ans+sgn*res)%mod;        }        for(int j = k,p = m;j >= 1;j--,p--){            ans = (ans*p%mod)*inv[j]%mod;        }        printf("Case #%d: %lld\n",I++,(ans+mod)%mod);//注意这里一定要(+mod)%mod,因为前面做过减法,有可能为负数。    }    return 0;}