5 条题解
-
0
按原规则枚举行走(通过子任务 1,共 4 分)
适用于子任务 ,即 。
维护已经访问的普通节点集合 、当前节点 、前一个节点 和当前喜欢的颜色 ,直接按题目规则搜索下一步。到达 时将颜色设为蓝色,到达 时设为红色。
定义 为从这一状态继续行走并最终停在 的方案数,其中 是到达当前节点并完成变色后的颜色。若 ,可以选择立即结束,因此先计入一个方案,但仍然要继续枚举下一步。
记 为到达 后的颜色: 时为蓝色, 时为红色,其余情况保持 。令 为所有满足以下条件的下一节点 :,,有向边 的颜色为 ,并且 时有 。转移为
$$F(S,u,p,c)=[u=2]+\sum_{v\in A(S,u,p,c)}F(S',v,u,h(v,c)),$$其中 时 ,否则 。答案为 , 表示不存在前一个节点。所有加法对 取模。
代码用访问标记实现集合:进入一个普通节点时标记,递归返回后撤销;节点 不设置普通节点访问标记。前一个节点单独保留,用来排除立即折返。每条合法行走对应搜索树中唯一的一个在 处选择结束的位置,故不会重计或漏计。
搜索一定有限。两个相邻普通节点之间至多出现两个特殊节点,否则三个连续特殊节点只能形成 或 。普通节点总共最多使用 个,递归深度为 。
令 。时间复杂度可上界为 :先选择并排列用到的普通节点,每个间隙中的特殊节点序列只能为空、、、 或 ;每个搜索状态再枚举至多 个后继。空间复杂度为 ,包括访问标记和递归栈。该方法不合并相同状态,在较大的全白普通节点输入上会超时。
参考代码(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; vector<int> used(n); auto dfs = [&](auto&& self, int u, int last, bool blue) -> int { if (u == 0) blue = true; if (u == 1) blue = false; int res = (u == 1); for (int v = 0; v < n; v++) { if (v == u || v == last || (v >= 2 && used[v])) continue; bool edge_blue = (u < v) != (s[u] == s[v]); if (edge_blue != blue) continue; if (v >= 2) used[v] = 1; res += self(self, v, u, blue); if (res >= mod) res -= mod; if (v >= 2) used[v] = 0; } return res; }; cout << dfs(dfs, 0, -1, true) << '\n'; } int main() { cin.tie(0)->sync_with_stdio(0); solve(); }
信息
- ID
- 2313
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 9
- 标签
- (无)
- 递交数
- 7
- 已通过
- 1
- 上传者