传送门:
http://agc010.contest.atcoder.jp/tasks/agc010_f
题意:
给出一棵N个节点的树,每个节点有Ai个石头
Takahashi先选择一个节点v放置一个棋子,然后Takahashi和Aoki轮流操作(Takahashi先)
.从棋子所在节点移走一个石头
.将棋子移到与当前节点相邻的节点
不能进行操作者输(当棋子所在节点没有石头的时候不能进行操作),求所有能够使Takahashi赢的节点v
思路:
找规律得:
把棋子从权值小的点,移动到权值大的点毫无意义,分分钟被对手赶回来。。。。。
既然如此,顺着数列的单调性把树走一遍。。。。。。
sg[x]: 棋子的起点在u,在u的子树中游戏,先手是否能胜利 (0:败 1:胜)。
对于节点u的所有儿子v: if(a[v] < a[u] && sg[v] == 0) sg[u] = 1;
证明:
a): 当u存在一个儿子v,满足 a[v] < a[u] && sg[v] == 0 时。
因为,sg[v]=0,所以从v开始,在v的子树中游戏,先手必败。
因为 a[v] < a[u],所以在节点u开始且先手的那位可以把棋子移动到v,
强迫对手在v的子树游戏,若对手把棋子从v移回u,将棋子再从u移回v即可。
b): 若u不存在这样的儿子,因为无法转移到必败态,所以此时,sg[u] = 0
因为不会回头,所以可以直接这么dp!(无后效性是成立的)
对所有点dfs一遍就搞定了
代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
#define ff first
#define ss second
#define pb push_back
#define ll long long
#define mod 1000000007
#define ull unsigned long long
#define mst(ss,b) memset(ss,b,sizeof(ss));
#define pl(x) cout << #x << "= " << x << endl;
const int inf = 0x3f3f3f3f;
const int N = 3e3+10;
vector<int>E[N];
int n, a[N], sg[N];
void dfs(int u, int fa){
sg[u] = 0;
for(auto v : E[u]){
if(v == fa || a[v] >= a[u])continue;
dfs(v, u);
if(!sg[v])sg[u] = 1;
}
}
int main(){
scanf("%d", &n);
for(int i=1; i<=n; i++)scanf("%d", &a[i]);
for(int i=1; i<n; i++){
int u, v; scanf("%d%d", &u, &v);
E[u].pb(v);
E[v].pb(u);
}
for(int i=1; i<=n; i++){
dfs(i, -1);
if(sg[i])printf("%d ", i);
}
return 0;
}