hdu6143 Killer Names 容斥+排列组合

/**
题目:hdu6143 Killer Names
链接:http://acm.hdu.edu.cn/showproblem.php?pid=6143
题意:有m种字符(可以不用完),组成两个长度为n的字符串,要求这两个字符串含有的字符没有相同的。
求有多少种方式组成这两个字符串。
思路:容斥+排列组合
反思一开始以为这题是dp,然后想了很久没想出来,觉得挺不好处理的,,能力不足。
后来想到是容斥。
f[n][1]表示长度为n的字符串用1种字符填充的方法数。
f[n][2] = 2^n - C(2,1)*f[n][1]; 两种的所有填充方式-一种的填充方式。
f[n][3] = 3^n - C(3,1)*f[n][1] - C(3,2)*f[n][2];...
...
f[n][m] = m^n - sigma[1<=i<m]C(m,i)*f[n][i];

那么可以枚举左边这个n长度字符串的组合方式用去i种字符,那么剩下那个字符串用剩下的字符任意组合即可。
注意m大于n的情况。
*/

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 2005;
const int mod = 1e9 + 7;

LL fac[N], inv[N];
LL f[N][N];
void init()
{
    inv[0] = inv[1] = 1;
    for(int i = 2; i < N; i++)inv[i] = (mod-mod/i)*inv[mod%i]%mod;
    for(int i = 2; i < N; i++)inv[i] = inv[i-1]*inv[i]%mod;
    fac[0] = 1;
    for(int i = 1; i < N; i++) fac[i] = fac[i-1]*i%mod;
}
LL Pow(LL a,LL b)
{
    LL p = 1;
    while(b){
        if(b&1) p = p*a%mod;
        a = a*a%mod;
        b >>= 1;
    }
    return p;
}
LL C(int n,int m)
{
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void solve(int n,int m)
{
    for(int i = 1; i <= min(n,m); i++){
        f[n][i] = Pow(i,n);
        for(int j = 1; j < i; j++){
            f[n][i] = (f[n][i]-f[n][j]*C(i,j)%mod+mod)%mod;
        }
    }
}
int main()
{
    int T;
    int n, m;
    init();
    cin>>T;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        solve(n,m);
        LL ans = 0;
        int mis;
        if(m>n) mis = n;
        else mis = m-1;
        for(int i = 1; i <= mis; i++){
            ans = (ans+C(m,i)*f[n][i]%mod*Pow(m-i,n)%mod)%mod;
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

posted on 2017-08-17 19:11  hnust_accqx  阅读(214)  评论(0编辑  收藏  举报

导航