题目描述

给定一棵 $N$ 个顶点的树,顶点 $i$ 上有 $A_i$ 枚石子。

Alice 和 Bob 在玩游戏。首先,Alice 可以选择树上的一个顶点并在其上放一枚棋子,然后,Alice 先手,交替进行如下操作:

请找出所有的点 $v$,使得当 Alice 开局时将棋子放到点 $v$ 时,她有必胜策略。

输入格式

第一行包含一个正整数 $N$ ($2 \leq N \leq 3000$),表示树的点数。

第二行包含 $N$ 个非负整数 $A_1, A_2, \cdots, A_N$ ($0 \leq A_i \leq 10^9$),表示顶点 $i$ 上初始的石子数。

接下来 $N - 1$ 行,每行两个正整数 $a_i, b_i$ ($1 \leq a_i, b_i \leq N$),描述树上的一条边。保证这 $N - 1$ 条边恰好构成一棵树。

输出格式

输出一行,包含若干个整数,表示所有满足条件的 $v$,从小到大输出。若不存在满足条件的 $v$,则什么也不输出。

题解

考虑对树上每一个顶点进行判断。

下面证明一个结论:任何时刻,任何人都不会往 $A_u \geq A_v$ 的点 $u$ 移动 (设当前棋子在点 $v$)。

证明

不妨设 Alice 这么移动了,即将棋子从 $v$ 移动到 $u$。

将原树看作以 $v$ 为根的有根树,那么考虑以 $u$ 为根的子树:

  1. 若 Bob 有必胜策略,那他显然就赢了。

    这是因为,树是二分图,因此当棋子在点 $u$ 时,一定是轮到 Bob 操作,而他可以控制使得棋子不走出这棵子树。

  2. 若 Bob 无必胜策略,那么 Bob 此时的最优策略显然是逃出这棵毒瘤子树 $u$。

    于是此时 Bob 会将棋子从 $u$ 移动到 $v$。

    而 $A_u \geq A_v$,因此这样反复循环必为 Alice 先失去策略,

于是 Alice 使用这条策略不可能取胜,所以她不会这样走。

于是每个人只会向满足 $A$ 更小的地方走 —— 此时,按照上述分析可知,如果子树 Bob 赢,那么原树 Alice 输;反之如果子树 Bob 输,那么原树 Alice 就赢了 (因为 $A$ 更小,Bob 是无法 "逃" 回去的)

这样,就转化为了一般的博弈问题:

  1. 若一个状态 (点) 为 N 状态 (必胜态),当且仅当它可以走到一个状态 (子树),使得其为 P 状态。

  2. 若一个状态 (点) 为 P 状态 (必败态),当且仅当它无路可走,或走到的所有状态 (子树) 均为 N 状态。

通过上述两条博弈的基本性质递归计算即可,注意时刻需要满足绿色性质,因为不满足的子树会对你产生 "立即失败" 的效果,谁走都不讨好。

单次判定只需 dfs 一遍,是 $O \left( N \right)$ 的,总时间复杂度 $O \left( N^2 \right)$。

代码

#include <bits/stdc++.h>

const int N = 3054, M = N * 2;

int n, E = 0;
int to[M], first[N], next[M];
int a[N];

inline void addedge(int u, int v) {
	to[++E] = v, next[E] = first[u], first[u] = E;
	to[++E] = u, next[E] = first[v], first[v] = E;
}

bool dfs(int x, int px = 0) {
	int i, y; bool r = true;
	for (i = first[x]; i && r; i = next[i])
		if ((y = to[i]) != px && a[y] < a[x]) r &= dfs(y, x);
	return !r;
}

int main() {
	int i, u, v; bool first = true;
	scanf("%d", &n);
	for (i = 1; i <= n; ++i) scanf("%d", a + i);
	for (i = 1; i < n; ++i) scanf("%d%d", &u, &v), addedge(u, v);
	for (i = 1; i <= n; ++i) if (dfs(i)) first ? first = false : putchar(32), printf("%d", i);
	return putchar(10), 0;
}

坑1:注意失败条件是 $A_u \geq A_v$ 而不是 $A_u > A_v$。