Cleaning题解

\(Cleaning\) 题解

题意:一颗树上每个结点有一个权值,每次可以选择两个叶子结点将其之间的点权减 \(1\) ,判断是否可以通过若干次操作将点权全部变为 \(0\)

\(sol\) :我们首先考虑叶子结点的父亲,权值设为 \(x\) ,而他的所有儿子权值和为 \(y\) ,如果一次这些叶子中一次操作和外部的叶子结点配对,那么可以将 \(x\) 减一, \(y\) 减一。如果是内部的两个叶子结点则可以将 \(x\) 减一,\(y\) 减二,然后在这里可以用小学奥数鸡兔同笼的思路解出,应该有多少对内部进行操作,然后剩下的可以合并为一个点进行考虑,因为其他叶子和它们操作时并不关心具体是哪个叶子。具体实现直接 \(dfs\) ,然后把答案反向推回去,代码非常好写。

code:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
typedef long long ll;
inline ll rd()
{
    char c;ll f=1;
    while(!isdigit(c=getchar()))if(c=='-')f=-1;
    ll x=c^48;
    while(isdigit(c=getchar()))x=(x<<1)+(x<<3)+(c^48);
    return x*f;
}
int tov[N<<1],nxt[N<<1],hd[N],cnt,d[N],n,rt=1;
void add(int u,int v){nxt[++cnt]=hd[u],tov[cnt]=v,hd[u]=cnt;};
ll w[N];
bool ans=1;
ll dfs(int u,int fa)
{
    ll sum=0,cnt=0,M=0;
    for(int i=hd[u];i;i=nxt[i])if(tov[i]!=fa)
    {
        ll t=dfs(tov[i],u);
        sum+=t,M=max(M,t),cnt++;
    }
    if(cnt==0) return w[u];
    if(cnt==1)
    {
        if(sum!=w[u]) ans=0;
        return sum;
    }
    if(sum-w[u]<0||2*w[u]-sum<0||M>w[u]) ans=0;
    return 2*w[u]-sum;
}
int main()
{
    n=rd();
    for(int i=1;i<=n;i++) w[i]=rd();
    for(int i=0;i<n-1;i++)
    {
        int u=rd(),v=rd();
        add(u,v),add(v,u);
        d[u]++,d[v]++;
    }
    if(n==1){printf("%s\n",w[1]?"NO":"YES");return 0;}
    if(n==2){printf("%s\n",w[1]==w[2]?"YES":"NO");return 0;}
    for (int i=1;i<=n;i++) if(d[i]>1) rt=i;
    if(dfs(rt,0)!=0) ans=0;
    printf("%s\n",ans?"YES":"NO");
    return 0;
}
posted @ 2024-12-18 07:59  Re_Star  阅读(19)  评论(1)    收藏  举报