恰好 k 对手套要分三步:选完整对、选剩余单只所在对、再选左右手。
P11250 [GESP202409 八级] 手套配对
GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。
一、先回答问题
直接 C(2n,m) 再扣非法情况很容易重叠计数。
二、核心观察
完整对贡献 2 只;剩余 m-2k 只必须来自不同的剩余对。
为什么不丢答案:三步选择互不重叠,且每种合法取法唯一对应这三步。
完整对
剩余单只
左右选择
三、互动演示
步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推
点击下一步开始。
四、自己选答案
为什么剩余每对只能取一只?
先判断,再点按钮。
什么时候答案为 0?
先判断,再点按钮。
五、手推结果
| 选择阶段 | 方案数 | 含义 |
|---|---|---|
| 完整对 | C(n,k) | 确定 k 对 |
| 单只来源 | C(n-k,m-2k) | 不能再成对 |
| 左右 | 2^(m-2k) | 每对二选一 |
课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。
六、C++14 参考代码
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int MAXN = 2005;
long long C[MAXN][MAXN];
long long p2[MAXN];
void precompute() {
// 预处理组合数
for (int i = 0; i < MAXN; i++) {
C[i][0] = 1;
for (int j = 1; j <= i; j++) {
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
}
}
// 预处理 2 的幂
p2[0] = 1;
for (int i = 1; i < MAXN; i++) {
p2[i] = (p2[i - 1] * 2) % MOD;
}
}
void solve() {
int n, m, k;
cin >> n >> m >> k;
int remaining_gloves = m - 2 * k;
int remaining_pairs = n - k;
// 如果需要取的手套数少于凑成 k 对所需,或者剩余要取的手套数超过剩余对数
if (remaining_gloves < 0 || remaining_gloves > remaining_pairs) {
cout << 0 << "\n";
return;
}
long long ans = C[n][k]; // 选出 k 对
ans = (ans * C[remaining_pairs][remaining_gloves]) % MOD; // 从剩下对里选剩下的手套
ans = (ans * p2[remaining_gloves]) % MOD; // 每只都有左右两种可能
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
precompute();
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}