【gdgzezoi】Problem C: Cleaning

Description

有一棵n个点的树,第i个节点有ai个石子。

判断是否有可能去掉所有的石头。

每次都可以选择一对不同的叶子节点,这对叶子节点路径上的所有点都必须要有石子。然后去掉这两个叶子节点路径上的每个节点中的一颗石子(选择的两个叶子节点也是路径中的点)。叶子节点就度数为1的点。
Input

第一行一个整数 n

第二行n个整数ai

接下来 n−1 行每行两个整数 x,y,表示节点 x 和节点 y 之间有边相连
Output
输出:YES或NO
Sample Input
sample input 1:
5
1 2 1 1 2
2 4
5 2
3 2
1 3

sample input 2:
3
1 2 1
1 2
2 3

sample input 3:
6
3 2 2 2 2 2
1 2
2 3
1 4
1 5
4 6
Sample Output
sample output 1:
YES

sample output 2:
NO

sample output 3:
YES
HINT

2≤n≤105

1≤ai≤109

思路

直接选一个非叶子的节点做根,然后dfs一遍,处理出每棵子树需要往外走多少次。

对于某个点x,记他被经过了ax次,他的子树各需要往外走x1,x2,x3,…,xk次,其中k是x的儿子个数。那在ax中,有的是由两个不同儿子中的叶子之间的路径绕过x而产生的,有的是从某个叶子走出x的子树而产生的。设前者数量为t,则根据整棵子树往外走的次数等于每个儿子走出x子树的次数之和,有ax−t=(∑xi)−2t,解得t=(∑xi)−ax。

接下来就是判断能不能重复t次选两个不同的xi,xj,并把两个都减一。
找出最大的x,根据剩下的和是否大于这个最大值,来判断出最后会剩下多少。

代码

#include
#define ll long long
using namespace std;
const int N=1e5+77;
struct E
{
    int to,next,id;
}e[N*2];
int n,root,a[N],d[N]={0},ls[N]={0};
ll f[N];
bool dfs(int u,int fa,int pre)
{
    if(d[u]==1)
    {
        f[pre]=a[u];
        return 1;
    }
    ll sum=0;
    for(int i=ls[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa)
            continue;
        if(!dfs(v,u,e[i].id)||f[e[i].id]>a[u])
            return 0;
        sum+=f[e[i].id];
    }
    f[pre]=(a[u]<<1)-sum;
    if(u==root) return f[pre]==0;
    else return f[pre]>=0&&f[pre]<=a[u];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    if(n==2) return puts(a[1]==a[2]?"YES":"NO"),0;
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        d[u]++;d[v]++;
        e[i]=(E){v,ls[u],i};ls[u]=i;
        e[n+i]=(E){u,ls[v],i};ls[v]=n+i;
    }
    for(int i=1;i<=n;i++)
        if(d[i]>1)
        {
            root=i;break;
        }
    return puts(dfs(root,0,0)?"YES":"NO"),0;
}

你可能感兴趣的:(题解)