2017多校八 1011题 hdu 6143 Killer Names 排列组合

题目链接


题意:

用 m 种颜色给两块 n 个格子的板涂色,要求同一种颜色不能同时出现在两块板上。


思路:

枚举两块板总共使用的颜色数 tot (2 <= tot <= m),

再枚举第一块板使用的颜色数 i,因为要求每种颜色都必须出现,所以有 1 <= i <= n && 1 <= tot - i <= n.

接下来的问题就是用 i 种颜色给 n 个格子涂色(每种都必须用到)有多少种涂法.

记 A[n][i] 为用 i 种颜色给 n 个格子涂色的方法数,则 

A[n][1] = 1, A[n][n] = n!, A[n][i] = i * A[n - 1][i - 1] + A[n - 1][i] * i,

理解为

1. 用 i - 1 种颜色给 n - 1 个格子涂色, 那么第 n 个格子必须涂剩下的一种颜色,即 A[n - 1][i - 1] * 1, 又前面选择出 i - 1 种颜色,故该种情况有 C(i, i - 1) * A[n - 1][i - 1] = i * A[n - 1][i - 1]

2. 用 i 种颜色给 n - 1 个格子涂色,那么剩下的一个格子可以涂 i 种颜色中的任意一种,故该种情况有 A[n - 1][i] * i.

加起来即为 A[n][i] = i * A[n - 1][i - 1] + A[n - 1][i] * i.

ans 即为 C[m][tot] * C[tot][i] * A[n][i] * A[n][tot - i] 累和(C 与 A 通过预处理求得)


Code:

#include <bits/stdc++.h>
#define maxn 2000
typedef long long LL;
const LL mod = 1e9 + 7;
LL C[maxn + 10][maxn + 10], A[maxn + 10][maxn + 10];
inline int max(int a, int b) { return a > b ? a : b; }
inline int min(int a, int b) { return a < b ? a : b; }
void init() {
    for (int i = 0; i <= maxn; ++i) C[i][i] = C[i][0] = 1;
    for (int i = 2; i <= maxn; ++i) {
        for (int j = 1; j < maxn; ++j) C[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod;
    }
}
void init2() {
    for (int i = 1; i <= maxn; ++i) A[i][1] = 1;
    for (int i = 2; i <= maxn; ++i) A[i][i] = A[i-1][i-1] * i % mod;
    for (int i = 3; i <= maxn; ++i) {
        for (int j = 2; j < i; ++j) {
            A[i][j] = (A[i - 1][j] * j % mod + A[i - 1][j - 1] * j % mod) % mod;
        }
    }
}
void work() {
    LL n, m;
    scanf("%lld%lld", &n, &m);
    LL ans = 0;
    for (int tot = 2; tot <= m; ++tot) {
        int le = max(1, tot - n), ri = min(tot - 1, n);
        for (int i = le; i <= ri; ++i) {
            int j = tot - i;
            ans += C[m][tot] * C[tot][i] % mod * A[n][i] % mod * A[n][tot - i] % mod;
            ans %= mod;
        }
    }
    printf("%lld\n", ans);
}
int main() {
    init(); init2();
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值