UVA-7040(容斥+快速幂)

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;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值