题解:P4096 [HEOI2013] Eden 的博弈树
whisper_Luo · · 题解
原题链接
题意
有一棵
非叶子节点胜利规则:
-
如果该节点的决策方是黑方,想要它是黑胜,那么只要有一个儿子是黑胜,这个节点就是黑胜。反之亦然。
-
反过来,如果该节点的决策方是黑方,想让它是白胜,就必须所有儿子都是白胜。反之亦然。
定义“最小黑方胜集合”:选一些叶子设为黑胜,能让根变成黑胜,且选的叶子数量最少的叶子的集合。“最小白方胜集合”同理。
关键叶节点即为既属于某个最小黑方胜集合,又属于某个最小白方胜集合的叶子节点、
现在需要求出编号最小的关键叶节点、关键叶节点数量、关键叶节点的编号异或和。
思路
设 其实 )胜所需的最少叶子数。叶子本身代价为
非叶子节点按决策方与目标是否相同分两种情况转移:
-
若
col_u = c :f_{u, c} = \min\{f_{v, c}\} 。 -
否则
col_u \ne c :所有儿子都必须可达,f_{u, c} = \sum f_{v, c} 。若任一儿子f_{v, c} = \infty ,则f_{u, c} = \infty 。
两次 dfs 分别求出
那么我们就可以回溯找出可能属于最小集合的叶子节点了。从根出发 dfs,若当前节点
用两个数组
时空复杂度
时间复杂度:
空间复杂度:
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 5;
const int INF = 1e9;
int n, f[N][2], col[N], vis[2][N], ans, cnt;
vector<int> g[N];
void dfs(int u, int c) {
bool flag = 0;
for (int v : g[u]) {
col[v] = col[u] ^ 1;
dfs(v, c);
if (col[u] == c) {
f[u][c] = min(f[u][c], f[v][c]);
} else if (f[v][c] == INF) {
f[u][c] = INF;
flag = 1; // col[u] != c 且有儿子节点不可达,则该节点也不可达(无解)
} else if (!flag) { // 必须有解才可以累加该节点的答案
f[u][c] = f[u][c] == INF ? 0 : f[u][c];
f[u][c] += f[v][c];
}
}
}
void Dfs(int u, int c) {
if (f[u][c] == INF) return; // 不可达
for (int v : g[u]) {
if (g[v].empty()) { // 可达的叶子即在最小叶节点集合中
vis[c][v] = 1;
continue ;
}
if (col[u] == c) {
if (f[v][c] == f[u][c]) {
Dfs(v, c);
}
} else {
Dfs(v, c);
}
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for (int i = 2, x; i <= n; i++) {
cin >> x;
g[x].push_back(i);
}
for (int i = 1; i <= n; i++) {
f[i][0] = f[i][1] = g[i].empty() ? 1 : INF;
}
dfs(1, 0), dfs(1, 1);
Dfs(1, 0), Dfs(1, 1);
for (int i = 1; i <= n; i++) {
if (vis[0][i] && vis[1][i]) {
ans ^= i, cnt++;
if (cnt == 1) {
cout << i << " ";
}
}
}
cout << cnt << " " << ans;
return 0;
}
/*
少年心动是仲夏野草的荒原,割不完,烧不尽。长风一吹,野草遍连了天。
哥……好久不见。
七号路依旧长得没有尽头,梧桐荫还是枝繁叶茂。
人间骄阳正好,风过林梢,彼时他们正当年少。
*/