HDU 6143 排列组合

来源:互联网 发布:php面向对象几大原则 编辑:程序博客网 时间:2024/05/21 05:55

排列组合 - DP

题意:

​ 给出m字母,现在要求用m个字母去取名字,firstname 和 lastname 不能同时存在相同字母,问:会有多少种组合数,对于firstname 和 lastname 必须保证长度为n。

​ 数据量0<n,m<2000

题意:

​ 最开始最开始用的DP去推出所有的解,复杂度达到了n3 ,平常对复杂度的不重视导致比赛时候频频犯错,用到了超时算法,以至于到后来一直去想优化,无论怎么优化没有思考对原始计算的公式进行:公式优化。失败的题目,没写出也是一种对不重视复杂度的惩罚,认,下次努力。

​ 若想求出所有的可能免不了要分情况,对于第一name,里面的字母数量有限制,可以从1枚举,但是每次枚举需要求出i种字母恰好填满n个位置的排列数,这个可以通过预处理,定义:dp[i] 表示i种字母恰好填满n个位置的总数,那么当求出i的时候可以先算出总的可能数,减去不满足条件的总数即可,总数即为in : 对于每一个位置都有i种情况,不满足恰好填满n个位置的情况就是C(i,j)dp[j]:(j<i) 即少于i种的总情况。

#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn = 2005;const int mod = 1000000007;typedef long long LL;LL dp[maxn];LL n,m;const int maxk = 2000 + 5;LL C[maxk][maxk];void table_C(){    memset(C, 0,sizeof(C));    C[0][0] = 1;    for(int i = 0; i <= maxk-5; i++){        C[i][0] = C[i][i] = 1;        for(int j = 1; j < i; j++)            C[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod;    }}//快速幂LL pow(LL a, LL x, LL p = mod) {    LL res = 1;    while(x) {        if (x & 1) res = res * a % p;        x >>= 1;        a = a * a % p;    }    return res;}void DP(int n){    dp[1] = 1;    for(int i = 2;i <= n; i++) {        LL temp = 0;        for(int j = 1;j < i; j++) {            temp = (temp + C[i][j]*dp[j]%mod)%mod;        }        LL temp1 = pow(i,n);        dp[i] = (temp1-temp+mod)%mod;    }}int main(int argc, char const *argv[]){    // freopen("in.txt","r",stdin);    table_C();    int tt;    scanf("%d",&tt);    while(tt--) {        scanf("%I64d%I64d",&n,&m);        int Max;        if(n >= m) Max = m-1;        else Max = n;        DP(n);        LL ans = 0;        for(int i = 1;i <= Max; i++) {            LL temp1 = C[m][i]*dp[i]%mod;            LL temp2 = pow(m-i,n);            ans = (ans + temp1*temp2%mod)%mod;        }        printf("%I64d\n",ans%mod);    }    return 0;}
原创粉丝点击