[R10D]合成球


/*
题目链接: https://bs.daimayuan.top/p/58
题目名称: #58. [R10D]合成球
题目类型: 动态规划、组合数学
时空限制: 1秒/512MB
难度: 8
题目描述
有 n 个球排成一行,球的颜色分为白和黑两种。
进行 n−1 轮合成,每一轮你选择两个位置相邻的球合成为一个新球,新球的颜色由你从合成前这两个球的颜色中选择。
分别求最终合成白球和黑球的不同合成方案各有多少种,对 998244353 取模。
如果两个方案,在某一轮合成选择的两个球的位置不同,或该轮合成的新球的颜色不同,视为两个不同合成方案。
输入格式
第一行包含一个整数 n 表示球的数量。
第二行包含一个仅由 w 和 b 组成的长度为 n 的字符串 S 表示每个球的颜色,其中 w 表示白球 b 表示黑球。
输出格式
输出一行包含两个整数,分别表示最终合成白球的不同合成方案数量和最终合成黑球的不同合成方案数量。
样例
样例输入 #1:
3
wwb
样例输出 #1:
3 2
样例解释 #1:
最终合成白球的合成方案有 3 种:
①第一轮将第 1 个球和第 2 个球合成为白球,第二轮将第 1 个球和第 2 个球合成为白球;
②第一轮将第 2 个球和第 3 个球合成为白球,第二轮将第 1 个球和第 2 个球合成为白球;
③第一轮将第 2 个球和第 3 个球合成为黑球,第二轮将第 1 个球和第 2 个球合成为白球。
最终合成黑球的合成方案有 2 种:
①第一轮将第 1 个球和第 2 个球合成为白球,第二轮将第 1 个球和第 2 个球合成为黑球;
②第一轮将第 2 个球和第 3 个球合成为黑球,第二轮将第 1 个球和第 2 个球合成为黑球。

输入:
27
wwbwbwwbbwwbwbwwbbwwbwbwwbb
输出:
112681370 131030915

输入:
1
b
输出:
0 1

对于100% 的数据,1≤n≤400。数据保证S 仅由 w 和 b 组成。

2 
ww
*/


#include <iostream>




using namespace std;

const long long MOD = 998244353;
const int N = 410;
char arr[N];
long long dp[N][N][2]; // dp[i][j][k] 表示从 i 到 j 的区间合成成 k 颜色的方案数
int n;


long long C[N * 2][N * 2];
void initC() {
	for (int i = 0; i < N ; 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;
		}
	}
}


int main()
{
	initC();
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> arr[i];
	}

	for (int len = 1; len <= n; len++) {
		for (int color = 0; color < 2; color++) {
			for (int i = 1; i + len - 1 <= n; i++) {
				int j = i + len - 1;
				if (len == 1) {
					if (color == 0 && arr[i] == 'w') // 白球
						dp[i][j][color] = 1;
					else if (color == 1 && arr[i] == 'b') // 黑球
						dp[i][j][color] = 1;
					else
						dp[i][j][color] = 0;
					continue;
				}

				for (int k = i; k < j; k++) {
					int left = k - i + 1;
					int right = j - k;
					long long comb = C[left + right - 2][left - 1];
					//当前可合成的颜色是黑  前黑*后黑  前白*后黑  前黑*后白  合成颜色为白类推
					dp[i][j][color] += (dp[i][k][color] * dp[k + 1][j][color]) % MOD* comb%MOD;	dp[i][j][color] %= MOD;
					dp[i][j][color] += (dp[i][k][color^1] * dp[k + 1][j][color]) % MOD * comb % MOD;	dp[i][j][color] %= MOD;
					dp[i][j][color] += (dp[i][k][color] * dp[k + 1][j][color^1]) % MOD * comb % MOD;	dp[i][j][color] %= MOD;
				}
			}
		}
	}

	cout << dp[1][n][0] << " " << dp[1][n][1] << endl; // 输出最终合成白球和黑球的方案数

	return 0;
}
//区间dp  左边合成黑白球的方案乘以右边合成黑白球的方案数,  还要额外考虑 左边x-1球和右边y-1个球的合成方案数 可以左边点n下右边点n下,不一定是左边合成完再去右边合成。
//所以有 插板法(x+y-2)中点击选择出x-1中的组合方案数 C(x+y-2,x-1)
 

posted on 2025-07-09 12:34  itdef  阅读(7)  评论(0)    收藏  举报

导航