题目链接:http://exam.upc.edu.cn/problem.php?cid=1367&pid=3
题意:给一棵树,树上的每个结点都有自己的权值a[i],问操作任意次后能不能把所有结点的权值全部变为0。操作:选取任意两个叶子节点,并把连接这两个叶子节点的路上的节点的权值全部减一。
要求:如果一条路上已经有零权值的结点,不能对这条路操作。
思考:对于一个父亲节点,操作可以分为两种:①选取的两个叶子一个是自己的儿子(假设有x1次)。②两个叶子都是自己的儿子(假设有x2次)。
则有x1+x2=a[i];x1+2*x2=sum;x1>=0;x2>=0;儿子的权值的最大值要小于父亲节点权值。其中sum为节点i的儿子的权值和。
x2次②操作后,叶子节点只剩下一个有非零权值且权值小于等于父亲节点剩下的权值。此时的叶子节点可以忽略,父亲节点变成叶子节点。递归,最后判断根节点权值是否为零即可。
1 #include<bits/stdc++.h> 2 3 const int maxn = 1e5+6; 4 int n,a[maxn]; 5 std::vector<int> E[maxn]; 6 void dfs(int x,int p) 7 { 8 if((int)E[x].size() == 1)return; 9 long long sum = 0; 10 long long mx = 0; 11 for(int i=0; i<(int)E[x].size(); i++) 12 { 13 int v = E[x][i]; 14 if(v==p)continue; 15 dfs(v, x); 16 sum += a[v]; 17 mx = std::max(1ll*a[v], mx); 18 } 19 if(a[x]>sum||sum>2*a[x])///x1,x2>=0 20 { 21 puts("NO"); 22 exit(0); 23 } 24 int k=sum-a[x];///只有内部消化的时候需要改变 25 if(a[x]<mx)///max<a[x] 26 { 27 puts("NO"); 28 exit(0); 29 } 30 a[x]-=k; 31 } 32 int main() 33 { 34 // freopen("in.txt","r",stdin); 35 scanf("%d", &n); 36 for(int i=0; i<n; i++) 37 scanf("%d", &a[i]); 38 for(int i=1; i<n; i++) 39 { 40 int x,y; 41 scanf("%d%d", &x, &y); 42 x--,y--; 43 E[x].push_back(y); 44 E[y].push_back(x); 45 } 46 if(n==2) 47 { 48 if(a[0]==a[1])puts("YES"); 49 else puts("NO"); 50 return 0; 51 } 52 int v=0; 53 while(E[v].size() == 1) 54 v++; 55 dfs(v, -1); 56 if(a[v]==0) 57 puts("YES"); 58 else 59 puts("NO"); 60 }