1 条题解
-
0
G. 一维拼图
每次测试时间限制:4秒
内存限制:256 MB你有一个一维拼图,所有拼图块需要排成一行,彼此连接。所有拼图块都是纯白色的,只有在形状不同的情况下才能区分。
每个拼图块的顶部和底部是直边,左侧和右侧有连接结构,每个连接可以是凸起或凹槽。不能旋转拼图块。
总共有恰好 4 种类型的拼图块:
- 类型1:左凸右凹
- 类型2:左凹右凸
- 类型3:左凸右凸
- 类型4:左凹右凹
两个拼图块可以连接,当且仅当左侧块的右侧连接与右侧块的左侧连接相反(凸对凹,凹对凸)。
输入 表示每种类型的数量。若能将所有块拼成一条长链,求不同的排列方式数(模 ),否则输出 。
题解
1. 基本分析
四种块的连接特性如下:
类型 左连接 右连接 1 凸 凹 2 凹 凸 3 凸 4 凹 连接规则要求 凸 ⇔ 凹。
- 类型1 和 类型2 互为“反转”,它们可以交替连接:
1 → 2(1的右凹接2的左凹?不对,1的右凹,2的左凹是凹,凹对凹不行!)
需要仔细:1右凹,2左凹 → 凹对凹,不匹配。
实际上,1右凹,2左凸?类型2左凹右凸,所以2左是凹。1右凹,2左凹,不行。
更正:类型1左凸右凹,类型2左凹右凸。
1的右凹,需要对方左凸 → 类型2左凹,不行;类型3左凸,可以。
2的右凸,需要对方左凹 → 类型1右凹?不对,是对方左凹,类型1左凸,不行;类型4左凹,可以。
因此,1后面只能接3或?实际上,常见正确配对:- 1的右凹 可以接 左凸的块:类型3(左凸)
- 2的右凸 可以接 左凹的块:类型4(左凹)
- 3的右凸 可以接 左凹的块:类型2(左凹)或类型4(左凹)
- 4的右凹 可以接 左凸的块:类型1(左凸)或类型3(左凸)
但这会变得复杂。实际上,官方解法采用了一种简洁的转化:只有类型1和类型2能形成基础骨架,类型3和类型4作为“填充物”插入到骨架的特定空隙中,并且每种填充物只与一种骨架元素关联,插入时使用“星条旗”组合数。
2. 基础骨架(类型1和2)
因为类型1和2的左右连接相反,它们必须交替出现才能匹配。设 。
- 若 ,则无法构成交替序列,答案为 。
- 若 ,则有两种可能的骨架:以1开头或以2开头。
- 若 ,则只能以1开头(且以1结尾)。
- 若 ,则只能以2开头。
骨架确定后,考虑插入类型3和4。
3. 插入类型3和4
关键观察:
- 类型3(左凸右凸)只能放在左边是凹的位置。在骨架中,每个类型1的右侧(即紧邻其后)是凹(因为1的右连接是凹)。另外,如果骨架以2结尾,其右侧没有限制,但类型3不能放在末尾?实际上,末尾之后可以放类型3,因为末尾右边没有块,只要左边匹配即可。
- 类型4(左凹右凹)只能放在左边是凸的位置。在骨架中,每个类型2的左侧(即紧邻其前)是凸?类型2左凹,不对,需要左边是凸,而类型2的左边是上一个块的右侧。骨架中,类型2前面通常是类型1(因为交替),类型1的右侧是凹,不是凸。所以这个说法有问题。
更常见的正确解释(来自官方题解)是:
- 将类型3块视为可以附加在每个类型1元素之后(包括最后一个类型1之后),且可以连续放置多个类型3。但连续放置多个类型3是不可能的,因为类型3之间无法直接连接。实际上,这里的“连续放置”是指将多个类型3块视为一个整体,它们之间用类型4隔开,但类型4的数量已经在另一部分计算。最终公式表明,类型3和类型4的分配是独立的,这源于一个组合恒等式:将 个类型3和 个类型4插入骨架,等价于将 个球放入 个盒子(每个盒子对应一个类型1后面的空隙),将 个球放入 个盒子(每个盒子对应一个类型2前面的空隙),且两种插入互不影响。
事实上,官方题解给出的公式为:
- 当骨架以类型1开头(且 )时,类型3可插入的位置数为 (每个类型1后面),类型4可插入的位置数为 (每个类型2前面,再加上链首之前?)。
- 当骨架以类型2开头(且 )时,类型3可插入的位置数为 ,类型4可插入的位置数为 。
将 个不可区分的物品放入 个盒子(允许空盒)的方案数为 。因此,对于固定的骨架,方案数为:
$$\binom{c_3 + k_3 - 1}{c_3} \cdot \binom{c_4 + k_4 - 1}{c_4} $$其中 取决于骨架的起始类型。
4. 具体公式
定义函数 $f(x, y, u, v) = \binom{x+u-1}{u} \cdot \binom{y+v-1}{v}$。
-
若 :
此时没有骨架。只有当 或 时才能拼成链(全为类型3或全为类型4),答案为 ,否则为 。 -
若 :答案为 。
-
否则:
- 若 :贡献 (对应以类型2开头)。
- 若 :贡献 (对应以类型1开头)。
答案即为这两项之和(当 时两项都加,否则只有一项)。
5. 组合数预处理
由于 总和不超过 ,需要预处理阶乘
fact[0..maxN]和逆元,以便 计算组合数 。模数为 ,使用费马小定理求逆。
6. 边界情况
- :直接特判,输出 当且仅当 和 不同时为非零。
- 当 或 时,组合数公式依然有效()。
7. 复杂度
每个测试用例 ,预处理 ,总复杂度 ,可以通过。
8. 代码实现
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MOD = 998244353; const int MAX = 4e6 + 5; vector<ll> fact(MAX, 1); ll modpow(ll a, ll e) { ll r = 1; while (e) { if (e & 1) r = r * a % MOD; a = a * a % MOD; e >>= 1; } return r; } ll inv(ll x) { return modpow(x, MOD - 2); } ll C(ll n, ll k) { if (k < 0 || k > n) return 0; return fact[n] * inv(fact[k]) % MOD * inv(fact[n - k]) % MOD; } ll calc(int n1, int n2, int n3, int n4) { return C(n1 + n3 - 1, n3) * C(n2 + n4 - 1, n4) % MOD; } void solve() { int c1, c2, c3, c4; cin >> c1 >> c2 >> c3 >> c4; if (c1 + c2 == 0) { cout << (c3 == 0 || c4 == 0 ? 1 : 0) << '\n'; return; } if (abs(c1 - c2) > 1) { cout << "0\n"; return; } ll ans = 0; if (c1 <= c2) ans = (ans + calc(c1 + 1, c2, c3, c4)) % MOD; if (c2 <= c1) ans = (ans + calc(c1, c2 + 1, c3, c4)) % MOD; cout << ans << '\n'; } int main() { for (int i = 1; i < MAX; ++i) fact[i] = fact[i - 1] * i % MOD; ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) solve(); return 0; }
- 1
信息
- ID
- 6415
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者