UVA 19889 组合数学

UVA 19889
题目链接:
http://www.bnuoj.com/v3/problem_show.php?pid=13014
题意:
1-n的排列,然后问多少个排列前m个中有且仅有k个在原来位置上。
思路:
组合数学,参考了别人代码。
对于前面确定的k个,可以在最后答案时乘上C[m][k]表示选取方案,现在假设已经确定哪些数。然后表示剩下的m-k个都不在自己位置上的方案,注意不是n-k个,自己写的时候卡在这里。
然后就是简单的容斥原理~

注意组合数的求法用C[i][j] = C[i-1][j-1] + C[i-1][j],好写而且方便。
源码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#define LL long long
#define mod (1000000007)
const int MAXN = 1000 + 5;
LL rev[MAXN], fac[MAXN];
LL C[MAXN][MAXN];
LL ppow(LL a, int x)
{
    LL ans = 1;
    for(int i = x ; i ; i >>= 1){
        if(i & 1) ans = (ans * a) % mod;
        a = (a * a) % mod;
    }
    return ans;
}
void init()
{
    for(int i = 0 ; i < MAXN ; i++) rev[i] = ppow(i, mod - 2);
    C[0][0] = 1;    ///此处少会WA
    for(int i = 1 ; i < MAXN ; i++){
        C[i][0] = 1;
        for(int j = 1 ; j <= i ; j++) C[i][j] = C[i][j - 1] * (i - j + 1) % mod * rev[j] % mod;
    }
// C[0][0] = 1;
// for(int i = 1 ; i < MAXN ; i++){
// C[i][0] = 1;
// for(int j = 1 ; j <= i ; j++) C[i][j] = (C[i-1][j-1] + C[i-1][j]) % mod;
// }
    fac[0] = 1;
    for(int i = 1 ; i < MAXN ; i++) fac[i] = (fac[i - 1] * i) % mod;
}
int main()
{
    init();
    int T;
    scanf("%d", &T);
    for(int cas = 1 ; cas <= T ; cas++){
        int n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        LL ans = 0;
        int flag = 1;
        for(int i = 0 ; i <= m - k ; i++){
            ans = (ans + C[m - k][i] * fac[n - k - i] * flag) % mod;
// printf("i = %d, ans = %I64d\n", i, ans);
            flag = -flag;
        }
        ans = (ans % mod + mod) % mod;
        ans = (ans * C[m][k]) % mod;
        printf("Case %d: ", cas);
        cout << ans << endl;
    }
    return 0;
}

你可能感兴趣的:(UVA 19889 组合数学)