JZOJ 5448. 【NOIP2017提高A组冲刺11.3】机房比教室好多了

Description

这里有一个N 个点的树, 节点从1 到N 编号, 第i 条边连接了ai 和bi.
一开始第i 个点上有Ai 个石头. Takahashi 和Aoki 会玩一个游戏.
首先, Takahashi 会选择一个出发点; 然后, 从Takahashi 开始, 他们会轮流进行如下操作:
• 首先, 从当前的点上拿走一个石头.
• 然后, 走到一个相邻的点上.
当一个玩家无法执行操作时, 他就输了. 请你找到所有可以让Takahashi 获胜的出发点.

Input

N
A1 A2 ::: AN
a1 b1 …
aN−1 bN−1

Output

按升序输出一行表示可以让Takahashi 获胜的出发点.

Sample Input

输入1:

3
1 2 3
1 2
2 3

输入2:

5
5 4 1 2 3
1 2
1 3
2 4
2 5

输入3:

3
1 1 1
1 2
2 3

Sample Output

输出1:

2

输出2:

1 2

输出3:

(输出为空)

Data Constraint

对于30% 的数据, N <=7;Ai <= 6;
对于60% 的数据, N <= 2000;
对于100% 的数据, 1 <= N <= 10^6, 1 <= ai, bi <= N; 0 <= Ai <= 10^9, 保证输入的是一棵树.

Solution

  • 设布尔数组 bz[i] 表示节点 i 是否可行(0 不可行,1 可行)。

  • 我们可以发现一个显然的结论:对于一个节点 x 和它的儿子节点 y

  • 如果满足 A[x]>A[y] bz[y]=0 ,则有 bz[x]=1

  • 因为我可以不断从 x 走到 y ,不断把对方“压”到失败点。

  • 那么这样就能从下往上处理出 bz 数组,即能 O(N^2) 得出答案。

  • 由于换一次根( O(1) ),子树答案不会改变,只有原根与新根的关系改变了。

  • 所以直接判断其是否符合上述条件即可,时间复杂度 O(N)

Code

#include<cstdio>
using namespace std;
const int N=1e6+1;
int tot;
int first[N],next[N<<1],en[N<<1];
int a[N];
bool bz[N];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline void insert(int x,int y)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
inline void dfs(int x,int y)
{
    for(int i=first[x];i;i=next[i])
        if(en[i]!=y)
        {
            dfs(en[i],x);
            if(!bz[x] && a[en[i]]<a[x]) bz[x]=!bz[en[i]];
        }
}
inline void find(int x,int y)
{
    for(int i=first[x];i;i=next[i])
        if(en[i]!=y)
        {
            if(!bz[x] && a[en[i]]>a[x]) bz[en[i]]=true;
            find(en[i],x);
        }
}
int main()
{
    int n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        insert(x,y);
        insert(y,x);
    }
    dfs(1,0),find(1,0);
    for(int i=1;i<=n;i++)
        if(bz[i]) write(i),putchar(' ');
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值