5 条题解
-
0
满分解法(100 分)
在两个特殊节点处分段
只有节点 能改变喜欢的颜色,却可以被反复访问;其余节点至多访问一次。直接记录完整行走历史会产生大量重复状态。先将一次行走按每次到达 或 的位置切开,每段的内部只含普通节点,也可能没有普通节点。
一段从 出发时全走蓝边,从 出发时全走红边。由题面的染色规则,同一对节点的两个相反方向的边颜色恰好相反。因此把所有红色段反向,所有非空内部段都成为仅由普通节点组成的蓝色有向路径。下面称这样的非空路径为一条“链”,允许只有一个节点。
这些链使用的普通节点互不相交;没有使用的普通节点直接忽略。我们先统计无序的链集合,再恢复它们在原行走中的顺序。
链的首尾颜色决定它的用途
特殊节点的编号都小于普通节点。蓝边从特殊节点进入普通节点时,两端异色;从普通节点回到特殊节点时,两端同色。
以链的首节点颜色、尾节点颜色为类型,得到下表。方向均指已经统一为蓝色的方向。
链类型 补上特殊端点后的蓝色路径 原行走中的用途 在 处进行一段蓝色行走 反向使用,在 处进行一段红色行走 正向使用,或反向成为从 到 的红色段 不可作为最终片段 最后一行不能使用:正向需要从 走蓝边,反向需要从 走红边,两者都与该特殊节点设置的颜色冲突。但构造过程中必须保留 链,因为加入其他普通节点后,它可以变成允许的类型。
从无序链集合恢复行走
设一组链中没有 链,其余三类分别有 条,总数 。
首先给这些链排列顺序,有 种。链由实际的节点编号区分,即使首尾颜色相同,也不是不可区分的对象。每条 链可以选择蓝色正向或红色反向,共有 种方向选择;另外两类方向固定。
确定这些信息后,行走能否拼接?当前所在的特殊节点与下一条链要求的起点相同时,直接进入链;不同时,先走一条 的直接边。 是蓝边, 是红边,都符合出发节点设置的颜色。开头从 出发,结尾也用同样方式接到 。
每个空隙中的直接边只能这样唯一决定。连续走两条特殊节点之间的边会形成 或 ,题面已经禁止。其他位置也不会产生非法的立即折返:重复的普通节点已被排除;若中间是普通节点,沿同一对端点往返需要两种边颜色,而喜欢的颜色在中间没有改变。
反过来,从任何合法行走中按特殊节点切段,再反转红段,可以唯一恢复链集合、链顺序与各条 链的方向。所以这一对应不重不漏,每个无序链集合的贡献为
空链集合也有一种行走,即 ,与 一致。到达 后继续走出的合法行走,会由后续链自然计入。
为什么按编号从大到小加入节点
令 ,依次处理普通节点 。设刚要加入的节点为 ,其颜色为 。已处理的所有节点编号都大于 ,于是:
- 旧节点 是蓝边,当且仅当 与 同色。
- 是蓝边,当且仅当 与 异色。
因此,新节点能连接哪些链,只取决于链的首尾颜色,不再需要知道端点的具体编号。
定义
为处理完最大的 个普通节点后,从其中选择任意子集,组成互不相交的非空蓝链,且 四类链的条数恰好分别为 的无序链集合数。每条链保留其真实节点和内部顺序,状态只把具有相同四个计数的集合归并。
所有参数非负,且 。初始条件为
其他状态为 。所有运算对 取模。
在任一目标链集合中,删除本轮新节点 后,只有五种情况:它未被选择;它单独成链;它在链首;它在链尾;它在链内。最后一种情况会把一条链拆成两条不同的链。反过来,插入时分别对应跳过、单独建链、接到链首、接到链尾、连接两条不同的链。删除操作唯一,保证不会按不同构造顺序重复统计同一个集合。
完整转移
以下每行表示
$$f(i+1,\text{目标四元组})\mathrel{+}=f(i,a,b,c,d)\times\text{系数}.$$新节点是黑色
它可以接在黑色链尾之后,也可以接在白色链首之前。
目标四元组 系数 条件 无 第一行合并了不选新节点,以及接到某条 或 链尾的情况。第二行是建立单点 链。第三、四行分别把新节点接到 链的首部。第五行把一条 链和一条 链连成 链。
最后一行合并了三类连链:、、。它们都会使 链减少一条,系数分别为 。中间必须用 ,因为同一条链不能同时充当左右两条链,否则新节点会把它闭合成环。
新节点是白色
交换上述推导中的黑白颜色,得到:
目标四元组 系数 条件 无 最后一项同样扣除了将一条 链首尾闭合的 种非法选择。每次只从第 层更新第 层,不能让刚加入的节点被再次使用。
答案与小例子
处理完全部普通节点,只允许 。根据此前的恢复计数,答案为
$$\boxed{\sum_{\substack{b,c,d\ge0\\b+c+d\le m}} f(m,0,b,c,d)\,(b+c+d)!\,2^d}.$$预处理 到 及 到 即可。
以
BWWB为例,先处理黑色节点 ,可以跳过它,也可以暂时保留单点 链。再处理白色节点 时,后者能够通过“接到链首”转移变成 的 链。最终允许的链集合只有三种:空集合,贡献 ;只有单点链 ,类型为 ,贡献 ;只有链 ,类型为 ,贡献 。合计 。这个过程也说明,若在中间层直接删除 状态,会丢掉 。
复杂度
时间复杂度为 。第 层的非负四元组满足总和不超过 ,共有 个,每个状态只有常数次转移;所有层总计 个状态。
空间复杂度为 。实现保留完整历史层,但只按 的合法范围分配各维,不建立五个长度都为 的长方体。 时共保存 个计数值,阶乘和幂表另占 空间。
AC 代码(C++20)
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n; string s; cin >> n >> s; constexpr int mod = 1000000007; int m = n - 2; using layer = vector<vector<vector<vector<int>>>>; vector<layer> dp(m + 1); for (int i = 0; i <= m; i++) { dp[i].resize(i + 1); for (int bb = 0; bb <= i; bb++) { dp[i][bb].resize(i - bb + 1); for (int bw = 0; bb + bw <= i; bw++) { dp[i][bb][bw].resize(i - bb - bw + 1); for (int wb = 0; bb + bw + wb <= i; wb++) { dp[i][bb][bw][wb].resize(i - bb - bw - wb + 1); } } } } dp[0][0][0][0][0] = 1; for (int i = 0; i < m; i++) { for (int bb = 0; bb <= i; bb++) { for (int bw = 0; bb + bw <= i; bw++) { for (int wb = 0; bb + bw + wb <= i; wb++) { for (int ww = 0; bb + bw + wb + ww <= i; ww++) { int cur = dp[i][bb][bw][wb][ww]; if (cur == 0) continue; auto add = [&](int a, int b, int c, int d, int ways) { int& res = dp[i + 1][a][b][c][d]; res = (res + 1LL * cur * ways) % mod; }; if (s[n - 1 - i] == 'B') { add(bb, bw, wb, ww, 1 + bb + wb); add(bb + 1, bw, wb, ww, 1); if (wb > 0) { add(bb + 1, bw, wb - 1, ww, wb); add(bb, bw, wb - 1, ww, wb * (bb + wb + ww - 1)); } if (ww > 0) add(bb, bw + 1, wb, ww - 1, ww); if (bb > 0 && ww > 0) add(bb - 1, bw + 1, wb, ww - 1, bb * ww); } else { add(bb, bw, wb, ww, 1 + bw + ww); add(bb, bw, wb, ww + 1, 1); if (bw > 0) { add(bb, bw - 1, wb, ww + 1, bw); add(bb, bw - 1, wb, ww, bw * (bb + bw + ww - 1)); } if (bb > 0) add(bb - 1, bw, wb + 1, ww, bb); if (bb > 0 && ww > 0) add(bb - 1, bw, wb + 1, ww - 1, bb * ww); } } } } } } vector<i64> fact(m + 1, 1), pow2(m + 1, 1); for (int i = 1; i <= m; i++) { fact[i] = fact[i - 1] * i % mod; pow2[i] = pow2[i - 1] * 2 % mod; } i64 ans = 0; for (int bw = 0; bw <= m; bw++) { for (int wb = 0; bw + wb <= m; wb++) { for (int ww = 0; bw + wb + ww <= m; ww++) { ans = (ans + dp[m][0][bw][wb][ww] * fact[bw + wb + ww] % mod * pow2[ww]) % mod; } } } cout << ans << '\n'; } int main() { cin.tie(0)->sync_with_stdio(0); solve(); }
信息
- ID
- 2313
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 9
- 标签
- (无)
- 递交数
- 7
- 已通过
- 1
- 上传者