题面
先考虑出发点在x,先手玩以x为根这棵子树的答案。
首先不难证明,a[x]变大,不会变得更劣。
那我们考虑先手拿完一个石头后走向一个a[son]>=a[x]的儿子,这样肯定是不明智的,因为假如对手把你推回x,你当然不能和他一直这样推来推去(你肯定先死),所以你会选择去别的儿子,但这当然不如一开始就去别的儿子(因为a[x]更大)。
再考虑先手拿完一个石头后走向一个a[son]
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1000010;
int n,a[maxn],s[maxn];
bool f[maxn];
struct edge
{
int t;
edge *next;
}*con[maxn];
int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
return x;
}
void ins(int x,int y)
{
edge *p=new edge;
p->t=y;
p->next=con[x];
con[x]=p;
}
void dfs(int v,int fa)
{
for(edge *p=con[v];p;p=p->next)
if(p->t!=fa)
{
dfs(p->t,v);
if(a[p->t]<a[v]&&s[p->t]==0) s[v]++;
}
}
void dfs2(int v,int fa)
{
if(a[fa]<a[v]) s[v]+=(f[v]==0);
for(edge *p=con[v];p;p=p->next)
if(p->t!=fa)
{
f[p->t]=(s[v]-(a[v]>a[p->t]&&s[p->t]>0));
dfs2(p->t,v);
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
ins(x,y);
ins(y,x);
}
dfs(1,0);
for(int i=1;i<=n;i++)
if(s[i]>0) printf("%d ",i);
return 0;
}
考场怕爆栈dfs序版:
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1000010;
int n,a[maxn],s[maxn],fa[maxn],st[maxn],dfn[maxn],tim;
bool f[maxn];
struct edge
{
int t;
edge *next;
}*con[maxn];
int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
return x;
}
void ins(int x,int y)
{
edge *p=new edge;
p->t=y;
p->next=con[x];
con[x]=p;
}
void dfs()
{
int top=1;st[1]=1;fa[1]=0;
while(top>0)
{
int v=st[top--];
dfn[++tim]=v;
for(edge *p=con[v];p;p=p->next)
if(p->t!=fa[v])
{
fa[p->t]=v;
st[++top]=p->t;
}
}
for(int i=n;i>1;i--){int v=dfn[i];if(a[v]<a[fa[v]]&&s[v]==0) s[fa[v]]++;}
for(int i=2;i<=n;i++)
{
int v=dfn[i];
f[v]=(s[fa[v]]-(a[fa[v]]>a[v]&&s[v]>0));
if(a[fa[v]]<a[v]) s[v]+=(f[v]==0);
}
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
ins(x,y);
ins(y,x);
}
dfs();
for(int i=1;i<=n;i++)
if(s[i]>0) printf("%d ",i);
return 0;
}
其实并不需要这么麻烦。考虑一条边(x,y),x,y之间只会转移一次(因为a[x]
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e6+10;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
struct node{
int to,nxt;
}tree[N<<2];
int num[N],head[N],cnt=0,vis[N],n,can[N];
void add(int x,int y){
tree[++cnt]=node{y,head[x]};
head[x]=cnt;
}
int dfs(int u,int fa){
if (vis[u]) return can[u];
vis[u]=1;can[u]=0;
for (int i=head[u];i;i=tree[i].nxt){
int v=tree[i].to;
if (v==fa) continue;
if (num[u]>num[v]&&!dfs(v,fa)){
can[u]=1;
break;
}
}
return can[u];
}
int main(){
n=read();
for (register int i=1;i<=n;++i) num[i]=read();
for (register int i=1,x,y;i<n;++i)
x=read(),y=read(),
add(x,y),add(y,x);
for (register int i=1;i<=n;i++)
if (dfs(i,0)) printf("%d ",i);
}