您的位置:首页 > 其它

HDU6143 组合数学 递推

2017-08-18 10:36 260 查看
题意:每个名字有名和姓两部分,两部分长度都是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 = 1e9 + 7;
const int MAXN = 2000 + 10;
ll c[MAXN][MAXN];
ll f[MAXN];
ll mod_pow(ll x, ll n)
{
ll res = 1;
x = x % MOD;
while(n > 0)
{
if(n & 1) res = res *x % MOD;
x = x * x % MOD;
n >>= 1;
}
return res;
}
void Comb()
{
for(int i = 1; i < MAXN; 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;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

Comb();
int t; cin >> t;
while(t--)
{
int n, m; cin >> n >> m;
for(int i = 1; i <= n && i < m; i++)
{
f[i] = mod_pow((ll)i, (ll)n);
for(int j = i - 1; j >= 1; j--)
{
f[i] = (f[i] - c[i][j] * f[j] % MOD + MOD) % MOD;
}
}
ll ans = 0;
for(int i = 1; i <= n && i < m; i++)
{
for(int j = 1; 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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM