天天看點

HDU6143 組合數學 遞推

題意:每個名字有名和姓兩部分,兩部分長度都是n,給定m種字元,且名和姓中不能有同一種字元,問有幾種可能性?

思路:

如果n個長度用x個字元表達,那就相當于n個不同小球放在x個不同的盒子裡。設其結果為f(x),那麼f(x) = x ^ n - C(x, 1) * f(x - 1) - C(x, 2) * f(x - 2) …… - C(x, x - 1) * f(1).

然後就枚舉兩邊各用了多少種字元統計結果。

反思:

1、正難則反的思想;

2、不應太早就放棄,必須堅持;

3、大數相減記得先加MOD再%,每一次有可能超MOD的乘法就%一下。

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int MOD =  + ;
const int MAXN =  + ;
ll c[MAXN][MAXN];
ll f[MAXN];
ll mod_pow(ll x, ll n)
{
    ll res = ;
    x = x % MOD;
    while(n > )
    {
        if(n & ) res = res *x % MOD;
        x = x * x % MOD;
        n >>= ;
    }
    return res;
}
void Comb()
{
    for(int i = ; i < MAXN; i++)
    {
        c[i][] = c[i][i] = ;
        for(int j = ; j < i; j++)
        {
            c[i][j] = (c[i - ][j] + c[i - ][j - ]) % MOD;
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie();

    Comb();
    int t; cin >> t;
    while(t--)
    {
        int n, m; cin >> n >> m;
        for(int i = ; i <= n && i < m; i++)
        {
            f[i] = mod_pow((ll)i, (ll)n);
            for(int j = i - ; j >= ; j--)
            {
                f[i] = (f[i] - c[i][j] * f[j] % MOD + MOD) % MOD;
            }
        }
        ll ans = ;
        for(int i = ; i <= n && i < m; i++)
        {
            for(int j = ; j <= n && j <= m - i; j++)
            {
                ans += c[m][i] * f[i] % MOD * c[m - i][j] % MOD * f[j] % MOD;
                ans %= MOD;
            }
        }
        cout << ans << endl;
    }
}