AtCoder 2304 Cleaning

49 篇文章 0 订阅

http://www.elijahqi.win/archives/1367
Time limit時間制限 : 2sec / Memory limitメモリ制限 : 256MB

配点 : 700 点

問題文
N 頂点からなる木があり、頂点には 1 から N の番号がついています。 また、N−1 本の辺の内、i 番目の辺は頂点 ai と頂点 bi を結んでいます。

今、各頂点 i には Ai 個の石が置いてあります。 以下の操作を繰り返して、全ての石を取り除くことができるか判定してください。

相異なる 2 つの葉を一組選ぶ。そして、その 2 頂点間のパス上にある頂点全てからちょうど 1 つ石を取り除く。
ただし、葉とは木の頂点で次数が 1 の頂点を指し、選んだ葉自体もパス上の頂点として考える。
石が置かれていない頂点がパス上にあるときは、その操作を行えないことに注意してください。

制約
2≦N≦105
1≦ai,bi≦N
0≦Ai≦109
与えられるグラフは木である。
入力
入力は以下の形式で標準入力から与えられる。

N
A1 A2 … AN
a1 b1
:
aN−1 bN−1
出力
全ての石を取り除くことができるなら YES を、そうでないなら NO を出力せよ。

入力例 1
Copy
5
1 2 1 1 2
2 4
5 2
3 2
1 3
出力例 1
Copy
YES
以下のようにすれば、すべての石を取り除くことができます。

葉として 4 と 5 を選ぶ。このとき、4 以外の頂点に石が 1 個残る。
葉として 1 と 5 を選ぶ。このとき、全ての頂点から石がなくなる。
入力例 2
Copy
3
1 2 1
1 2
2 3
出力例 2
Copy
NO
入力例 3
Copy
6
3 2 2 2 2 2
1 2
2 3
1 4
1 5
4 6
出力例 3
Copy
YES
Score : 700 points

Problem Statement
There is a tree with N vertices, numbered 1 through N. The i-th of the N−1 edges connects vertices ai and bi.

Currently, there are Ai stones placed on vertex i. Determine whether it is possible to remove all the stones from the vertices by repeatedly performing the following operation:

Select a pair of different leaves. Then, remove exactly one stone from every vertex on the path between those two vertices. Here, a leaf is a vertex of the tree whose degree is 1, and the selected leaves themselves are also considered as vertices on the path connecting them.
Note that the operation cannot be performed if there is a vertex with no stone on the path.

Constraints
2≦N≦105
1≦ai,bi≦N
0≦Ai≦109
The given graph is a tree.
Input
The input is given from Standard Input in the following format:

N
A1 A2 … AN
a1 b1
:
aN−1 bN−1
Output
If it is possible to remove all the stones from the vertices, print YES. Otherwise, print NO.

Sample Input 1
Copy
5
1 2 1 1 2
2 4
5 2
3 2
1 3
Sample Output 1
Copy
YES
All the stones can be removed, as follows:

Select vertices 4 and 5. Then, there is one stone remaining on each vertex except 4.
Select vertices 1 and 5. Then, there is no stone on any vertex.
Sample Input 2
Copy
3
1 2 1
1 2
2 3
Sample Output 2
Copy
NO
Sample Input 3
Copy
6
3 2 2 2 2 2
1 2
2 3
1 4
1 5
4 6
Sample Output 3
Copy
YES
题意 给出我们n个点的 一棵树我每次可以选择从两个叶子结点去走然后把这路径上的所有点都-1
那么怎么搞 我们可以知道最初我们可以考虑如果一个树根的子节点加起来也比我这个根要小显然是不可以的 如果比我要大 就有可能可行 如果我们直接合并的话可能存在错误样例 即:根为3 而子树分别为1和8 那么显然子树之间互相连边最大只有1条边 所以分析一波我们应该求出这个子树最大可以自己连多少边 然后如果还有剩余那么就是他们和外面的点连的边了 难在我们如何处理 最大能自连多少边 显然可以得到个结论 就是 当子树中最大的那个小于等于剩下的所有的话 就最多可以互相连完 (奇数还会剩1) 否则的话最多只有剩余的边数可以自己解决

为什么呢 考虑一个递归的过程 假设这个结论成立 那么我们每回都要求找一个最大找一个在剩余里最大的然后给他们– 那么好 这个过程相当于不断动态平衡的给最大值那一块 和剩余的一块删值 所以递归下去发现可以满足条件 我们删不完的情况一定是诸如 0 2的条件无法删掉 所以并不会出现 (口胡

所以我们每次找一下最大值 然后进行上述比较即可 然后新维护的点的值就是我底下多的部分-根权值 (这部分一定要自连 )然后减去就是我的出边 我要和其他连的边了

#include<cstdio>
#include<algorithm>
#define N 110000
using namespace std;
inline char gc(){
    static char now[1<<16],*T,*S;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while (ch<'0'||ch>'9') ch=gc();
    while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x;
}
struct node{
    int y,next;
}data[N<<1];
struct node1{
    int dep,id;
}vect[N];
int h[N],fa[N],n,in[N],a[N],num;
void dfs(int x){
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (fa[x]==y) continue;fa[y]=x;
        vect[y].dep=vect[x].dep+1;vect[y].id=y;
        if (in[y]==1) vect[y].dep=0;dfs(y);
    }
}
inline bool cmp(node1 a,node1 b){return a.dep>b.dep;}
int main(){
    //freopen("cleaning.in","r",stdin);
    n=read();
    for (int i=1;i<=n;++i) a[i]=read();
    if (n==2){if (a[1]==a[2]) printf("YES");else printf("NO");return 0;}
    for (int i=1;i<n;++i){
        int x=read(),y=read();
        data[++num].y=y;data[num].next=h[x];h[x]=num;in[y]++;
        data[++num].y=x;data[num].next=h[y];h[y]=num;in[x]++;
    }int root=1;while (in[root]==1) ++root;
    vect[root].dep=1;vect[root].id=root;dfs(root);
    sort(vect+1,vect+n+1,cmp);int op=1;
    //for (int i=1;i<=n;++i) printf("%d %d\n",vect[i].dep,vect[i].id);
    while (vect[op].dep!=0){
        int x=vect[op++].id;long long ans1=0;int max1=0,child=0,childn=0;
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y;if (fa[x]==y) continue;
            ans1+=a[y];max1=max(max1,a[y]);child++;
        }if (a[x]>ans1){printf("NO");return 0;}
        if (child==1) if (ans1!=a[x]){printf("NO");return 0;}
        long long rest=ans1-max1;
        if (rest>=max1){
            if (ans1-a[x]>(ans1>>1)){printf("NO");return 0;}else a[x]-=ans1-a[x];
        }else{
            if (ans1-a[x]>rest){printf("NO");return 0;}else a[x]-=ans1-a[x];
        }
    }
    if (a[root]==0) printf("YES");else printf("NO");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值