原题链接

题意简述

给出一棵n(n≤105)个节点的树,每个点有点权。每次可以选择两个叶节点并将连接它们的路径上的节点的点权-1(包括叶节点)。求能否将所有节点的点权都变为0。

分析



先考虑最简单的情况。在这种情况下,au必须等于av,否则GG。因为要想对v操作只能通过u,想对u操作只能通过v。

若相等我们可以令bu=av,并定义bu为:u需要往外连bu条路径。因为需要有au条路径进到以u为根的子树里面,可以看做u需要向外连au条路径。



再考虑一般情况。在这种情况下,au必须小于等于∑bv,否则GG。因为即使把bv都减完了au也不能为0,并且已经没有办法再减少au了。

若au=∑bv,直接从u往外连au条路径就好了。

和刚才不同,v之间可以自行解决一部分。比如操作v1−u−v2,可以让bu减1,让∑bv减2。我们可以进行类似的操作直到bu=∑bv,然后同上。

但是有可能没法让bu=∑bv,那就GG。那什么情况下不可行呢?

结论:当max{bv}≤(∑bv)/2时,可以把所有的bv消成0(或者剩一个1)。

证明

可能不对,看看就好

我们可以把问题反转一下:

对于一个零序列,每次对两个位置+1,能否得到目标序列?

显然的结论有当 max{bv}>(∑bv)/2 时会有 max{bv}−(∑bv−max{bv}) 加不出来。以及当 ∑bv 为奇数时至少会剩下一个。

下证对于其他情况:

额外创建两个位置,对它们进行无限次操作,意思就是足够多次。

{0,0,...,0} -> {0,0,...,0(,inf,inf)}

这时候我们加入了一种新操作:令这两个inf减1,也就是撤回一次。 然后我们可以做到 :

{0,0,...,0(,inf,inf)} -> {1,0,...,0(,inf+1,inf)} -> {2,0,...,0(,inf+1,inf+1)} -> {2,0,...,0(,inf,inf)}。

这样就有了构造方法:先两两给所有奇数填上1(要是有奇数个奇数就说明∑bv为奇数肯定会剩下,所以可以把一个奇数视为偶数),然后通过以上+2的操作把所有数都填好。最后对额外的两个位置一直-1减到0,这样就构造完成了。

如果在任何时候出现bu无法等于∑bv,那么GG。

以及,broot≠0也GG。

遍历所有节点复杂度为O(n),遍历每个节点的所有子节点复杂度为O(n),总时间复杂度为O(n)。

实现

首先以一个度不为1的点作为root,然后DFS出深度dpt和树的结构

由下到上将节点u和它的子节点v合并出bu,最后检查 broot

代码

//Cleaning
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long lint;
int const N=1e5+10;
int n,a[N];
int cnt,h[N],deg[N];
struct edge{
int u,v,nxt;
edge(int u1=0,int v1=0)
{
u=u1,v=v1;
nxt=h[u];
h[u]=cnt;
}
}ed[N<<1];
int root,fa[N],dpt[N];
struct rec{int dpt,id;} r[N];
bool cmpDpt(rec x,rec y) {return x.dpt>y.dpt;}
void dfs(int u)
{
for(int i=h[u];i!=0;i=ed[i].nxt)
{
int v=ed[i].v;
if(v==fa[u]) continue;
fa[v]=u,dpt[v]=dpt[u]+1;
dfs(v);
}
}
int main()
{
freopen("c.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
if(n==2)
{
if(a[1]!=a[2]) printf("NO");
else printf("YES");
return 0;
}
cnt=0; memset(h,0,sizeof h);
for(int i=1;i<=n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
ed[++cnt]=edge(u,v); deg[v]++;
ed[++cnt]=edge(v,u); deg[u]++;
}
for(int i=1;i<=n;i++)
if(deg[i]>1)
{
fa[i]=0,dpt[i]=1;
dfs(root=i);
break;
}
for(int i=1;i<=n;i++) r[i].dpt=dpt[i],r[i].id=i;
sort(r+1,r+n+1,cmpDpt);
for(int i=1;i<=n;i++)
{
int u=r[i].id;
if(deg[u]==1) continue;
int maxx=0; lint sum=0;
for(int j=h[u];j!=0;j=ed[j].nxt)
{
int v=ed[j].v;
if(fa[v]!=u) continue;
maxx=max(maxx,a[v]); sum+=a[v];
}
lint in=min(sum/2,sum-maxx);
if(a[u]>sum) {printf("NO"); return 0;}
else
{
if(sum-a[u]>in) {printf("NO"); return 0;}
else a[u]-=sum-a[u];
}
}
if(a[root]==0) printf("YES");
else printf("NO");
return 0;
}

注意

n=2时要特判一下,因为两个点都是叶节点会找不出root

AGC010 - C: Cleaning的更多相关文章

  1. 【AGC010 C】Cleaning

    题意 有一棵 \(n\) 个点的树,第 \(i\) 个节点有 \(a_i\) 个石子. 每次都可以选择一对不同的叶子节点,这对叶子节点路径上的所有点都必须要有石子.然后去掉这两个叶子节点路径上的每个节 ...

  2. Atcoder Grand Contest 010 C - Cleaning 树贪心(伪)

    C - Cleaning 题目连接: http://agc010.contest.atcoder.jp/tasks/agc010_c Description There is a tree with ...

  3. 【AtCoder】AGC010

    AGC010 A - Addition 如果所有数加起来是偶数那么一定可以,否则不行 #include <bits/stdc++.h> #define fi first #define s ...

  4. 【bzoj1672】[USACO2005 Dec]Cleaning Shifts 清理牛棚

    题目描述 Farmer John's cows, pampered since birth, have reached new heights of fastidiousness. They now ...

  5. Coursera-Getting and Cleaning Data-week1-课程笔记

    博客总目录,记录学习R与数据分析的一切:http://www.cnblogs.com/weibaar/p/4507801.html -- Sunday, January 11, 2015 课程概述 G ...

  6. Coursera-Getting and Cleaning Data-Week2-课程笔记

    Coursera-Getting and Cleaning Data-Week2 Saturday, January 17, 2015 课程概述 week2主要是介绍从各个来源读取数据.包括MySql ...

  7. Coursera-Getting and Cleaning Data-Week3-dplyr+tidyr+lubridate的组合拳

    Coursera-Getting and Cleaning Data-Week3 Wednesday, February 04, 2015 好久不写笔记了,年底略忙.. Getting and Cle ...

  8. Coursera-Getting and Cleaning Data-week4-R语言中的正则表达式以及文本处理

    博客总目录:http://www.cnblogs.com/weibaar/p/4507801.html Thursday, January 29, 2015 补上第四周笔记,以及本次课程总结. 第四周 ...

  9. 【BZOJ1672】[Usaco2005 Dec]Cleaning Shifts 清理牛棚 动态规划

    [BZOJ1672][Usaco2005 Dec]Cleaning Shifts Description Farmer John's cows, pampered since birth, have ...

随机推荐

  1. 亲测可用的国内maven镜像

    maven作为一个项目管理工具确实非常好用,之前oschina的中央仓库可用,现在oschina的maven服务器关了,于是自己倒腾了一个nexus,苦于自己的服务器是入门级的,下载速度实在让人着急. ...

  2. Java多线程之线程的同步

    Java多线程之线程的同步 实际开发中我们也经常提到说线程安全问题,那么什么是线程安全问题呢? 线程不安全就是说在多线程编程中出现了错误情况,由于系统的线程调度具有一定的随机性,当使用多个线程来访问同 ...

  3. 重拾Python(1):使用Anaconda搭建Python开发环境(Windows7)

    1.为什么选择Anaconda? Anaconda解决了Python使用痛点. Python好用但是令人头疼的就是库管理与Python不同版本的问题,特别是Windows环境下. 2.什么是Anaco ...

  4. 【转】Linux 服务器安全配置

    第一部分:RedHat Linux篇1.概述 Linux服务器版本:RedHat Linux AS 2.1 对于开放式的操作系统---Linux,系统的安全设定包括系统服务最小化.限制远程存取.隐藏重 ...

  5. 【转】vim取消高亮显示

    vim是vi的加强版. 进入vim或编辑完毕按esc后,输入/可帮助查找字符串,例如/main帮助查找main函数,找到的main高亮显示. 取消高量显示的两个办法: 1)按esc键,输入:nohl ...

  6. 浅谈TreeMap以及在java中的使用

    treemap结构是红黑树 1.先介绍一下平衡二叉树 其特点是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树.也就是说该二叉树的任何一个子节点,其左右子树的高度 ...

  7. 使用postMessage实现跨域 解决'Failed to execute 'postMessage' on 'DOMWindow''

    使用iframe+postMessage解决跨域问题,首先来过一遍其中的原理咯 原理: 发送方使用postMessage方法向接收方推送消息,第一个参数为推送的内容,第二个参数是允许被访问的域名: 接 ...

  8. Java - 二叉树递归与非递归

    树的定义具有递归特性,因此用递归来遍历比较符合特性,但是用非递归方式就比较麻烦,主要是递归和栈的转换. import java.util.Stack; /** * @author 李文浩 * @ver ...

  9. C#基础(五)--枚举的含义及其应用

    本章讲解如下枚举的如下几个知识点:    1.什么是枚举?    2.枚举是值类型还是引用类型?    3.如何定义枚举?    4.枚举的好处?    5.枚举在实际系统开发过程中的用处?    一 ...

  10. ABAP 程序中退出操作

    CHECK. 1)CHECK 后面要跟一个表达式,当表达式值为假(false)时,CHECK发生作用,退出循环(LOOP)或处理程序(Processing Block). 2)如果CHECK出现在循环 ...