[AGC010C] Cleaning

挺有意思的一道树形dp。

首先我们从叶子节点的父亲开始考虑,我们把路径分为两类,一类是从叶子节点往上去消除的路径,一类是叶子节点两两连接,内部解决的。

我们设\(f_x\)\(x\)号节点往上的路径条数,则对于这整棵子树我们又可以把它看做是它父亲的一个叶子节点,这样我们就把解决一棵树的问题改变成了计算叶子节点和他们父亲的问题。

现在唯一的问题就是怎么计算往上的路径。我们先设\(a_x\)\(x\)号节点有\(a_x\)个石头,\(sum\)为它的儿子们上传的路径总数,很显然,内部解决的路径共有\(a_x - f_x\)条。

则有: \(2 \times (a_x - f_x) + f_x = sum\)

所以有: \(f_x = 2 \times a_x - sum\)

我们惊奇地发现\(f_x\)其实是唯一确定的,由定义我们发现,如果合法则必然有\(0 \leq f_x\)\(f_x \leq a_x\),同时要特判一下一个儿子上传路径特别大的情况,大到哪怕所以上传都用它的上传,它剩余的上传量仍然比其它节点加起来都多的情况肯定也是不行的。

我们需要特判根节点,根节点一定要选不是叶子节点的点,还有如果根节点还有上传量,说明也是不合法的。

另外\(n = 2\) 的情况或许需要特判一下。

#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 5;
int n;
int head[MAXN] , to[MAXN << 1] , nxt[MAXN << 1] , cnt , ans , du[MAXN << 1];
LL f[MAXN] , a[MAXN];
void add(int u , int v) {nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;}
void dfs(int x , int fa) {
	LL _sum = 0 , mx = 0;
	for (int i = head[x]; i; i = nxt[i]) {
		if(to[i] == fa) continue;
		dfs(to[i] , x);
		if(ans) return;
		mx = max(mx , f[to[i]]);
		_sum += f[to[i]];
	}
	if(!_sum) {
		f[x] = a[x];
		return;
	}
	f[x] = 2ll * a[x] - _sum;
	if(f[x] < 0 || f[x] > a[x] || mx - f[x] > _sum - mx) {
		printf("NO");
		exit(0);
	}
}
signed main() {
	scanf("%lld" , &n);
	for (int i = 1; i <= n; ++i) scanf("%lld" , &a[i]);
	if (n == 2) return puts(a[1] == a[2] ? "YES" : "NO"), 0;
	int root = 0;
	for (int i = 1; i < n; ++i) {
		int u , v;
		scanf("%lld %lld" , &u , &v);
		du[u] ++;du[v] ++;
		add(u , v);add(v , u);
		if(du[u] > 1) root = u;
		if(du[v] > 1) root = v;
	}
	dfs(root , 0);
	if(f[root]) printf("NO");
	else printf("YES");
	return 0;
}
posted @ 2020-08-03 21:42  Reanap  阅读(125)  评论(0编辑  收藏  举报