1 条题解
-
0
思路与算法
按题图,上方有 行、每行 格,下方有 行、每行 格。固定上方放 个车,下方放 个车。
先从上方 行选出 行,再向这些行分配互异的 个可用列,方法数为 。这些车占用了 个左侧列;所有下方长行都包含它们,所以底部剩余可用列恰为 。从底部选 行并分配不同列,方法数为 。答案是
$$\sum_{i=0}^{\min(k,a,b)}\binom bi P(a,i)\binom d{k-i}P(a+c-i,k-i)\pmod{100003}.$$不合法的组合数或排列数定义为0。预处理至 的阶乘及逆阶乘; 且模数为素数,所有需要求逆的阶乘均可逆。
正确性说明
任一合法放置有唯一的上方车数 。选定上部行后,每行恰有一车,列必须互异,因此为这些已确定的行指定互异列对应 种位置。底部所有行都比上部宽,恰能使用除去已占 列后的其余 列,因此底部计数公式完整且不依赖上部占列的具体形状。
两部分行互不重合,剩余列选择保证全棋盘列也不冲突。反过来,式中每种选择均给出唯一合法车的位置集合,无需再给相同的车编号。按 的分类互不重叠,求和覆盖所有方案。阶乘计算公式与模逆条件成立,输出正确。
复杂度
预处理和初始化为 ,一次快速幂 ,枚举不超过 个上部车数,每项常数时间。总时间 ,额外空间 ,。不枚举棋盘格子或车的全排列。
易错点
- 上部是 列 行,下部是 列 行,不能只把棋盘看成两个互不影响的矩形。
- 底部可用列须减去上部已占列数 。
- 车不编号,选行后指定列已决定方案,不再乘 或 。
- 若 或剩余列不足,该项为0,避免负下标访问。
- 覆盖最小正尺寸、最大尺寸、Hall约束刚好可行的瘦长棋盘。候选的零尺寸不满足当前提示中所有参数≥1,整点拒用且原包保留。独立答案为逐行Ferrers棋盘DP,并与小棋盘完整车位置枚举核对。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; using ll = long long; const int MOD = 100003; ll power(ll a, ll e) { ll r = 1; while (e) { if (e & 1) r = r * a % MOD; a = a * a % MOD; e >>= 1; } return r; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int a, b, c, d, k; cin >> a >> b >> c >> d >> k; int size = max(a + c, max(b, d)); vector<ll> fact(size + 1), inv(size + 1); fact[0] = 1; for (int i = 1; i <= size; i++) fact[i] = fact[i - 1] * i % MOD; inv[size] = power(fact[size], MOD - 2); for (int i = size; i; i--) inv[i - 1] = inv[i] * i % MOD; auto C = [&](int n, int r) -> ll { if (r < 0 || r > n) return 0; return fact[n] * inv[r] % MOD * inv[n - r] % MOD; }; auto P = [&](int n, int r) -> ll { if (r < 0 || r > n) return 0; return fact[n] * inv[n - r] % MOD; }; ll answer = 0; for (int upper = 0; upper <= min(k, min(a, b)); upper++) { int lower = k - upper; ll ways = C(b, upper) * P(a, upper) % MOD * C(d, lower) % MOD * P(a + c - upper, lower) % MOD; answer = (answer + ways) % MOD; } cout << answer << '\n'; }
- 1