题意
有一个一维拼图,所有元素都需要排成一行,并相互连接,元素的类型正好有 4种,不能旋转,
如果左边元素的右边连接点与右边元素的左边连接点相对,那么两个元素就可以连接起来。谜题中每种类型包含 c1,c2,c3,c4 个元素。如果成功地将所有元素组合成一条长链,那么谜题就算完成了。问:有多少种方法可以做到这一点。
思路
前置知识(相同元素的分配问题):
1.n个相同的球放入m个盒中,盒不为空,则有多少种放法
插隔板,在n个球形成的 n-1个间隔中选 m-1个分别插入一个隔板,可分成m份且每份都不为空,即
2.n个相同的球放入m个盒中,盒可为空,则有多少种放法
将m-1个隔板和n个球看成相同元素排成一列,从(n+m-1)个中选(m-1)个位置来放隔板,即
本题思路:
将元素3看作凸,4看作凹(以右端为准),单独的3能连成一串凸的串,单独的4同理。而1、2可以实现“转向”,1可将凹变为凸,2反之,故1、2是以间隔的方式连接下去的,如…1…2…1…2…
根据1和2的数量分类,当c1==c2时,排列方式可能为1212…,也可能为2121…,则有奇数个空格 _1_2_1_2_, 3和4可放的间隔数不一样,两种情况相加:当abs(c1-c2)==1时,多出一个时,间隔排列形成的间隔数为偶数,3和4可放的间隔数一样;其他,即abs(c1-c2)>1时,不能组成一串,方案数为0。
特判,当c1==c2==0时,只有c3或c4一方为0或双方都为0时有1种排列方式,当c3、c4都不为0时,没有1、2作用不能连接在一起,不成串,有0种排列方式。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6 + 100, mod = 998244353;
int fac[N], infac[N];
int qsm(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1) res = res * a % p;
a = a * a % p;
k >>= 1;
}
return res;
}
//预处理出所给范围内的阶乘和阶乘的逆元
void pre(int n) {
fac[0] = infac[0] = 1;
for (int i = 1; i <= n; ++i)
fac[i] = fac[i - 1] * i % mod;
infac[n] = qsm(fac[n], mod - 2, mod);
for (int i = n - 1; i >= 1; --i)
infac[i] = infac[i + 1] * (i + 1) % mod;
}
//组合数的计算
int C(int a, int b) {
if (b > a) return 0;
return fac[a] * infac[b] % mod * infac[a - b] % mod;
}
void solve() {
int c1, c2, c3, c4;
cin >> c1 >> c2 >> c3 >> c4;
if (c1 == c2) {
if (!c1) {
if (!c3 || !c4) cout << 1 << '\n';
else cout << 0 << '\n';
return;
}
else {
int x = c1;
int ans1 = C(c3 + x - 1, x - 1) % mod * C(c4 + x, x) % mod;
int ans2 = C(c4 + x - 1, x - 1) % mod * C(c3 + x, x) % mod;
cout << (ans1 + ans2) % mod << '\n';
}
}
else if (abs(c1 - c2) == 1) {
int x = max(c1, c2);
int ans = C(c3 + x - 1, x - 1) * C(c4 + x - 1, x - 1) % mod;
cout << ans << '\n';
}
else cout << 0 << '\n';
return;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
pre(2e6 + 10);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}