agc010 F - Tree Game
题意:
给定一棵树,节点有权 \(a_i\)。初始甲选一个节点放上一个棋子,然后甲乙轮流操作(甲先),一次操作定义为:令当前棋子所在节点的权减一,然后把棋子移动到某邻点。不能操作(即轮到他时当前棋子所在节点权为0)者输。问甲一开始选哪些节点能赢
\(2\le n\le 3000\)
思路:
枚举甲一开始放棋子的点作为根节点。\(st_u=W/L\) 表示从 \(u\) 开始,只能在 \(u-\)子树中走,先手是必胜还是必败。那么
- 如果 \(u\) 存在一个儿子 \(v\),满足 \(a_v<a_u\) 且 \(st_v=L\),那么 \(st_u=W\)
- 否则,\(st_u=L\)
证明:
- 在该条件下,先手从 \(u\) 走到 \(v\),后手为了避免失败又走回 \(u\),这样重复下去由于 \(a_v<a_u\) 故先手必胜。注意 \(st_v=L\) 时减小 \(a_v\) 不会改变 \(st_v\)
- \(u\) 的儿子要么权值 \(\ge a_u\),那么后手每次走回 \(u\) 就能赢;要么是必胜点,那么后手往 \(v-\)子树中走就能赢
//代码就很简单了
bool dfs(int u, int fa) {
bool st = 0;
for(int v : G[u]) if(v != fa)
if(a[v] < a[u] && !dfs(v, u)) st = 1;
return st;
}
void sol() {
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i < n; i++) {
int x, y; cin >> x >> y;
G[x].pb(y), G[y].pb(x);
}
for(int i = 1; i <= n; i++)
if(dfs(i, 0)) cout << i << ' ';
}

浙公网安备 33010602011771号