AGC010
来源:互联网 发布:主力资金知乎 编辑:程序博客网 时间:2024/05/16 08:14
原题链接
题意简述
给出一棵
分析
先考虑最简单的情况。在这种情况下,
若相等我们可以令
再考虑一般情况。在这种情况下,
若
和刚才不同,v之间可以自行解决一部分。比如操作
但是有可能没法让
结论:当
证明
可能不对,看看就好
我们可以把问题反转一下:
对于一个零序列,每次对两个位置+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,这样就构造完成了。
如果在任何时候出现
以及,
遍历所有节点复杂度为
实现
首先以一个度不为1的点作为
由下到上将节点u和它的子节点v合并出
代码
//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;}