C. Two Arrays(1288C)(组合数)
题目来源:C. Two Arrays
题意:
给出 n 和 m,构造两个数组 a 和 b,满足以下条件
- 数组长度均为 m
- 两数组的值域为 1 - n
- a 数组按非降序排序
- b 数组按非升序排序
- a[i] <= b[i]
思路:
根据题意我们可以知道,a升序,b降序,并且 a[1] >= b[1],那么我们可以将b数组翻转一下,并将它们连接起来
将新的两个数组先连接起来组成 b[m], b[m - 1], … , b[1], a[1], a[2], … , a[m],此时的数组是非降序的
此时相当于在 1 - n 中选出 2 * m 个数,并且每个数可以被选多次
反向思考一下,就变成将 2 * m 个球投入 n 个篮子里,篮子允许为空
先让每个篮子至少拥有一个球
问题就变成了将 2 * m + n 个球投入 n 个篮子里,每个篮子至少有一个球
利用隔板法,将 n - 1 个板插入 2 * m + n - 1 个空隙中
因此答案为 C(2 * m + n - 1, n - 1)
思路来源
- 最后为什么直接插板就行,而不用考虑排序的问题(从前往后插板所隔开的球不能减少)
- 因为我们构成的新数组数非递减的,所以我们只需要找出 2 * m 个数即可,将它们存入新数组时,就相当于会自动排序了
AC代码
#include <bits/stdc++.h>
#define endl "\n"
#define rep(i, m, n) for (int i = (m); i <= (n); ++i)
#define rrep(i, m, n) for (int i = (m); i >= (n); --i)
#define IOS ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e3 + 50, mod = 1e9 + 7;
int n, m;
// 2 * m + n 个球,放入 n 个盒子,每个盒子至少一个
// 插板法相当于有 2 * m + n - 1 个空,插 n - 1 个板
// C(2 * m + n - 1, n - 1) = (2 * m + n - 1)! / (n - 1)! / (2 * m)!
ll qpow(int a, int b) {
ll res = 1; a %= mod;
while (b) {
if (b & 1) res = (ll)res * a % mod;
a = (ll)a * a % mod; b >>= 1;
}
return res;
}
ll C(int a, int b) {
ll up = 1, down = 1;
for (int i = 0; i < b; ++i) {
up = up * (a - i) % mod;
down = down * (b - i) % mod;
}
down = qpow(down, mod - 2); // 计算逆元
return up * down % mod;
}
void solve() {
cin >> n >> m;
cout << C(2 * m + n - 1, n - 1) << endl;
}
int main() {
solve();
return 0;
}