[题解] [AGC010F] Tree Game
题面
题解
我们取两个点出来分析, 发现如果先手要赢他就必须从他当前的 \(i\) 点走到 \(j\) 点, 满足\(a_i < a_j\)
不妨固定这棵树的根, 那么每一步都是从上往下走
因为从下往上走了一步其实是相当于抵消了从上往下走的那一步
相当于没有从上往下走, 两者的大小关系也没有变
所以我们就这样限制了操作的方向
又由于最后肯定会分出输赢来, 所以每一个点要么是先手必胜要么是先手必败
若这个点必胜, 当且仅当它下一步能够到达的所有点中中有至少一个先手必败的点
若这个点必败, 当且仅当它下一步能够到达的所有点中没有先手必败的点
设 \(f[i]\) 为 \(i\) 这个点是否先手必胜
那么有
\[f[u] |= !f[v], v \in {Son_u}
\]
对于每一个点作为根的情况都 DP 一次就可以覆盖所有情况了
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int N = 3005;
using namespace std;
int n, a[N], head[N], cnte, f[N];
struct edge { int to, nxt; } e[N << 1];
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * w;
}
inline void adde(int u, int v) { e[++cnte] = (edge) { v, head[u] }, head[u] = cnte; }
void dfs(int u, int fa)
{
for(int v, i = head[u]; i; i = e[i].nxt)
{
v = e[i].to; if(v == fa) continue;
dfs(v, u), f[u] |= !f[v] && (a[v] < a[u]);
}
}
int main()
{
n = read <int> ();
for(int i = 1; i <= n; i++)
a[i] = read <int> ();
for(int u, v, i = 1; i < n; i++)
{
u = read <int> (), v = read <int> ();
adde(u, v), adde(v, u);
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
f[j] = 0;
dfs(i, 0);
if(f[i]) printf("%d ", i);
}
puts("");
return 0;
}
这种跟博弈论有关的题做的还是太少了