题目描述
入侵军队采用了一种常见的战术:不直接进入城市,而是将其包围。军队将自己分成若干小队,以圆形方式在城市周围建立基地。为了控制城市内部,每三个小队被编为一组,负责覆盖一个三角形区域。将军的政策是确保任意两个三角形区域不重叠。
然而,入侵部队中有两种类型的军队:红军(Red Army\texttt{Red Army}Red Army)和黑军(Black Army\texttt{Black Army}Black Army)。每个小队只包含一种类型的军队。黑军明确意图为将军服务,但红军若有机会可能会背叛。因此决定每个三角组最多包含一个红军小队,以确保红军在任何分配中都不占主导地位。
给定 PPP 个小队( PPP 是 333 的倍数,且 2<P<402 < P < 402<P<40 )在圆形上的排列顺序和每个小队的颜色(R 表示红军,B 表示黑军),需要计算将所有这些小队划分成 P/3P/3P/3 个三角形组的方案数,满足以下条件:
- 每个小队恰好属于一个三角形组;
- 每个三角形组中红军小队的数量不超过 111 个;
- 任意两个三角形区域不重叠(即三角形的边不相交)。
由于起始位置是任意选择的,输入字符串可以是某个配置的任意旋转表示。
输入格式
第一行是一个整数 TTT ( T<100T < 100T<100 ),表示测试用例的数量。
每个测试用例包含两行:
第一行是一个整数 PPP ( 2<P<402 < P < 402<P<40 且 PPP 是 333 的倍数);
第二行是一个长度为 PPP 的字符串,由字符 ‘R’ 和 ‘B’ 组成,表示顺时针方向上各小队的颜色。
输出格式
对于每个测试用例,输出 Case X: Y ,其中 XXX 是测试用例编号, YYY 是满足条件的划分方案数。
样例输入
3
6
RBBBRB
6
BRBRBB
9
BBBBBBBBB
样例输出
Case 1: 2
Case 2: 2
Case 3: 12
题目分析
问题本质
本题要求将圆形上的 PPP 个点(每个点有颜色属性)划分成 P/3P/3P/3 个互不相交的三角形,且每个三角形中红色点的数量不超过 111 个。由于三角形的边不能相交,这种划分实际上形成了圆内的一种特定三角剖分结构。
关键观察
- 环形结构 :所有点在圆周上等距排列,形成一个凸多边形。
- 三角形不相交 :这意味着三角形的边不会交叉,因此整个划分可以看作是将多边形分解为多个三角形。
- 包含关系 :由于所有点都必须被覆盖,且三角形互不相交,任意一个点都必然属于某个三角形。特别地,我们可以固定一个点(例如点 000 ),那么必定存在一个包含点 000 的三角形。
解题思路
基于以上观察,我们可以采用 区间动态规划 (区间 DP\texttt{DP}DP)结合 记忆化搜索 ( DFS\texttt{DFS}DFS )来解决这个问题。核心思路如下:
1. 固定参考点
由于圆形具有旋转对称性,我们可以 固定点 000 作为参考点 。这样,任何有效的划分中都必然存在一个包含点 000 的三角形。设该三角形为 (0,a,b)(0, a, b)(0,a,b) ,其中 0<a<b<P0 < a < b < P0<a<b<P 。
2. 划分的三个弧段
三角形 (0,a,b)(0, a, b)(0,a,b) 将整个圆分成三个独立的弧段:
- 弧段 AAA :点 111 到点 a−1a-1a−1 (即从点 000 顺时针到点 aaa 之间的点,不包括端点)
- 弧段 BBB :点 a+1a+1a+1 到点 b−1b-1b−1 (即从点 aaa 顺时针到点 bbb 之间的点,不包括端点)
- 弧段 CCC :点 b+1b+1b+1 到点 P−1P-1P−1 (即从点 bbb 顺时针回到点 000 之间的点,不包括端点)
注意:每个弧段中的点都不包含三角形的顶点,因此这些弧段内部的划分是完全独立的。
3. 递归子问题
对于每个弧段,如果它非空,那么它内部的点也需要被划分成若干个三角形。这形成了一个 递归的子问题 ,其结构与原问题相同:给定一段连续的点(在圆周上的一段弧),计算将其划分成若干个不相交三角形的方案数,且每个三角形中红色点不超过 111 个。
4. 动态规划状态定义
设 dp[l][r]dp[l][r]dp[l][r] 表示弧段 [l,r)[l, r)[l,r) (左闭右开区间)的合法划分数。其中 lll 和 rrr 是在扩展字符串上的索引(为了处理环形,我们将原字符串复制一份拼接在后面)。
状态转移方程:
dp[l][r]=∑l<a<b<rtriangle(l,a,b) is validdp[l+1][a]×dp[a+1][b]×dp[b+1][r]
dp[l][r] = \sum_{\substack{l < a < b < r \\ \text{triangle}(l,a,b) \text{ is valid}}} dp[l+1][a] \times dp[a+1][b] \times dp[b+1][r]
dp[l][r]=l<a<b<rtriangle(l,a,b) is valid∑dp[l+1][a]×dp[a+1][b]×dp[b+1][r]
解释:
- 我们考虑包含左端点 lll 的三角形 (l,a,b)(l, a, b)(l,a,b) 。
- 该三角形将弧段 [l,r)[l, r)[l,r) 分成三个子弧段: [l+1,a)[l+1, a)[l+1,a) 、 [a+1,b)[a+1, b)[a+1,b) 和 [b+1,r)[b+1, r)[b+1,r) 。
- 三个子弧段的划分数相乘,并对所有有效的 (a,b)(a, b)(a,b) 求和。
边界条件:
- 如果弧段长度为 000 (即 l==rl == rl==r ),则 dp[l][r]=1dp[l][r] = 1dp[l][r]=1 (空弧段只有一种划分方式:不划分)。
- 如果弧段长度不是 333 的倍数,则 dp[l][r]=0dp[l][r] = 0dp[l][r]=0 (因为无法完整划分成三角形)。
5. 三角形有效性检查
对于三角形 (l,a,b)(l, a, b)(l,a,b) ,需要检查其中红色点的数量:
redCount=(color[l]==’R’)+(color[a]==’R’)+(color[b]==’R’)
\texttt{redCount} = (\texttt{color}[l] == \texttt{'R'}) + (\texttt{color}[a] == \texttt{'R'}) + (\texttt{color}[b] == \texttt{'R'})
redCount=(color[l]==’R’)+(color[a]==’R’)+(color[b]==’R’)
只有当 redCount≤1\texttt{redCount} \le 1redCount≤1 时,该三角形才是有效的。
6. 最终答案计算
对于整个环形,我们固定点 000 ,枚举所有可能的三角形 (0,a,b)(0, a, b)(0,a,b) ( 0<a<b<P0 < a < b < P0<a<b<P )。对于每个有效的三角形,总方案数为:
ways=dp[1][a]×dp[a+1][b]×dp[b+1][P]
\texttt{ways} = dp[1][a] \times dp[a+1][b] \times dp[b+1][P]
ways=dp[1][a]×dp[a+1][b]×dp[b+1][P]
将所有这样的 ways\texttt{ways}ways 累加即可得到最终答案。
注意:这里 dp[1][a]dp[1][a]dp[1][a] 对应弧段 [1,a)[1, a)[1,a) , dp[a+1][b]dp[a+1][b]dp[a+1][b] 对应弧段 [a+1,b)[a+1, b)[a+1,b) , dp[b+1][P]dp[b+1][P]dp[b+1][P] 对应弧段 [b+1,P)[b+1, P)[b+1,P) (即从点 b+1b+1b+1 到点 P−1P-1P−1 )。
算法复杂度
- 状态数: O(P2)O(P^2)O(P2) ,因为 lll 和 rrr 的取值范围均为 O(P)O(P)O(P) 。
- 每个状态转移需要枚举 aaa 和 bbb ,复杂度 O(P2)O(P^2)O(P2) 。
- 总时间复杂度: O(P4)O(P^4)O(P4) 。由于 P≤39P \le 39P≤39 , P4≈2.3×106P^4 \approx 2.3 \times 10^6P4≈2.3×106 ,在可接受范围内。
- 空间复杂度: O(P2)O(P^2)O(P2) ,用于存储 dpdpdp 数组。
实现细节
- 环形处理 :将原字符串 sss 复制一份得到 ring=s+s\text{ring} = s + sring=s+s ,这样弧段 [l,r)[l, r)[l,r) 可以直接用线性索引表示,无需处理模运算。
- 记忆化搜索 :使用递归函数实现动态规划,配合记忆化数组避免重复计算。
- 初始化 :对于每个测试用例,需要将 dpdpdp 数组初始化为 −1-1−1 ,表示未计算状态。
- 结果类型 :方案数可能较大,应使用
long long类型存储。
代码实现
// Independent Attacking Zones
// UVa ID: 11162
// Verdict: Accepted
// Submission Date: 2025-12-14
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 85;
string ring;
long long dp[MAXN][MAXN];
long long dfs(int l, int r) {
int gap = r - l;
if (gap == 0) return 1;
if (gap % 3 != 0) return 0;
if (~dp[l][r]) return dp[l][r];
long long &cnt = dp[l][r];
cnt = 0;
for (int a = l + 1; a < r; ++a) {
for (int b = a + 1; b < r; ++b) {
int redCount = (ring[l] == 'R') + (ring[a] == 'R') + (ring[b] == 'R');
if (redCount > 1) continue;
long long ways1 = dfs(l + 1, a);
long long ways2 = dfs(a + 1, b);
long long ways3 = dfs(b + 1, r);
cnt += ways1 * ways2 * ways3;
}
}
return cnt;
}
int main() {
int T; cin >> T;
for (int caseNo = 1; caseNo <= T; ++caseNo) {
int P; cin >> P;
string s; cin >> s;
ring = s + s;
memset(dp, -1, sizeof(dp));
long long cnt = 0;
for (int a = 1; a < P; ++a) {
for (int b = a + 1; b < P; ++b) {
int redCount = (ring[0] == 'R') + (ring[a] == 'R') + (ring[b] == 'R');
if (redCount > 1) continue;
long long ways1 = dfs(1, a);
long long ways2 = dfs(a + 1, b);
long long ways3 = dfs(b + 1, P);
cnt += ways1 * ways2 * ways3;
}
}
cout << "Case " << caseNo << ": " << cnt << '\n';
}
return 0;
}
总结
本题是一道典型的 环形区间动态规划 问题,关键在于:
- 利用圆形的对称性,固定一个参考点以简化问题;
- 将大问题分解为三个独立的子问题,通过递归求解;
- 使用记忆化搜索避免重复计算,提高效率。
通过合理定义状态和转移方程,我们可以在 O(P4)O(P^4)O(P4) 的时间复杂度内解决问题,对于 P≤39P \le 39P≤39 的数据规模完全可行。
9575

被折叠的 条评论
为什么被折叠?



