B题 | 小L的彩球

解题思路:

每队相邻的球之间都有线连着,可以看作是球从1号到n号从小到大排列着,相邻的球被线穿着。

当一部分在左边盒子,另一部分在右边盒子,有t条线露在外面时,意味着它们从连续的状态被切开成t+1堆,然后左右左右一堆堆放置(也可以是从右边开始放,右左右左)。

t+1堆球被这样依次堆叠在两边,其中一边有k1=(t+1)/2堆,另一边便是k2=t+1-k1堆,有可能是左边k1堆右边k2堆,也可能是左边k2堆右边k1堆,两种情况的答案之和就是最终答案。

以左边k1堆右边k2堆的情况为例,左边共k1堆,有x个球,右边有k2堆,有n-x个球。这相当于要解决把a个物品分成b堆的不同分发的个数的问题,是组合数的问题,可以用隔板法求解:a个物品中间有a-1个空,插上b-1个隔板,所以是要求C(a,b),用组合数板子就能搞定了,接着考虑一些特殊情况。如果把a个物品分成b堆,b>a,那肯定是不可行的,答案为0,如果b=0,此时因为a<=b(a>b的情况已经排除了),所以a=0,0个物品分0堆,答案是1。

示例代码:

#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;

const int N = 1e6+5;
const int mod = 998244353;
//从这里到第35行都是组合数板子的内容
int fact[N], infact[N];

int fac[N], ifac[N];  //预处理阶乘和阶乘的逆元
int qmi(int a, int b) { //快速幂
	int res = 1;
	while (b > 0) {
		if (b & 1) {
			res = res * a % mod;
		}
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

void init() {
	fac[0] = ifac[0] = 1;
	for (int i = 1; i < N; i++) {
		fac[i] = fac[i - 1] * i % mod;
		ifac[i] = ifac[i - 1] * qmi(i, mod - 2) % mod;
	}
}

int C(int a, int b) {  //求C(a,b)的组合数
	return fac[a] * ifac[b] % mod * ifac[a - b] % mod;
}

//下面是核心程序

int sv(int x, int k) {//把x个球分成k堆
	if (k > x) return 0;//是特判
	if (x == 0) return 1;//还是特判

	return C(x - 1, k - 1);
}

void solve() {
	int n, x, t;
	cin >> n >> x >> t;

	int k1 = (t + 1) / 2, k2 = t + 1 - k1; //t条线,一共t+1堆,一边是k1=(t+1)/2堆,另一边是k2=t+1-k1堆
	int ans1 = sv(x, k1) * sv(n - x, k2) % mod;//sv函数用于求解把x个球分成k堆的问题  sv(int x,int k)
	int ans2 = sv(x, k2) * sv(n - x, k1) % mod;

	int ans = (ans1 + ans2) % mod;

	cout << ans << endl;
}

signed main() {
	init();

	int T = 1;
	cin >> T;
	while (T--)
		solve();

	return 0;
}