[题解] [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; 
}

这种跟博弈论有关的题做的还是太少了

posted @ 2020-04-28 11:07  ztlztl  阅读(90)  评论(0编辑  收藏  举报