Loading

【题解】[CERC2013] Escape

给定一棵树,每个点上有权值 \(u_i\)\(|u|\le 10^6\),初始化 \(sum = 0\),每进入一个结点令 \(sum \leftarrow sum + u_i\),并且令 \(u_i\leftarrow 0\),一个点可以重复经过,问能否从 \(1\to t\)

非常妙的贪心模拟题。

我们先简单模拟以下,发现一个点 \(u_i \ge 0\),那么能走则走,如果 \(u_i <0\),那么我们还要考虑子树中 \(\ge 0\) 的点最终能否产生正的贡献。如果不能,那么整棵子树都不会经过。

那么我们就需要构造一种便于计算与合并的信息,维护上面的过程。对每个点记录 \((a,b)\) 表示如果到达点 \(i\)\(sum\ge a\),那么可以令 \(sum \leftarrow sum + b\)

这种信息可以很好用堆或者平衡树维护,如果两个标记满足 \(a_1 + b_1 \ge a_2\ge a_1\),那么我们可以把两个标记合并为 \((a_1,b_1 + b_2)\)

那么我们直接树上 DFS,然后合并子树的信息,如果当前点 \(u_i > 0\),就加入标记 \((0,u_i)\),否则我们就用集合中 \(a\) 最小的几个点将 \(u_i\) 变为正的收益。时间复杂度 \(\mathcal{O}(N\log^2 N)\),可以用可并堆做到单 \(\log\)

#define N 200005
int n, t; LL u[N]; vector<int>e[N];
struct node{
	LL a, b;
	bool operator<(const node o)const{
		return a > o.a;
	}
};
priority_queue<node>q[N];
void maintain(int x){
	while(si(q[x]) > 1){
		node l = q[x].top(); q[x].pop();
		node r = q[x].top();
		if(l.a + l.b >= r.a)q[x].pop(), q[x].push({l.a, l.b + r.b});
		else {q[x].push(l); break;}
	}
}
void dfs(int x,int fa){
	go(y, e[x])if(y != fa){
		dfs(y, x);
		if(si(q[x]) < si(q[y]))swap(q[x], q[y]);
		while(!q[y].empty())q[x].push(q[y].top()), q[y].pop();
	}
	if(u[x] > 0)q[x].push({0, u[x]});
	else if(u[x] < 0){
		LL k = -u[x];
		while(!q[x].empty()){
			maintain(x);
			node p = q[x].top(); q[x].pop();
			if(p.b > k){q[x].push({p.a + k, p.b - k}); break;}
			else k -= p.b;
		}
	}
}
void solve(){
	read(n, t);
	rp(i, n)read(u[i]);
	rp(i, n + 1)e[i].clear();
	rp(i, n - 1){
		int x, y; read(x, y);
		e[x].pb(y), e[y].pb(x);
	}
	u[++n] = 1e15, e[t].pb(n);
	dfs(1, 0);
	LL w = 0;
	while(!q[1].empty()){
		node cur = q[1].top(); q[1].pop();
		if(w >= cur.a)w += cur.b;
	}
	if(w >= 1e14)puts("escaped");
	else puts("trapped");
}
int main() {int T; read(T); while(T--)solve();}
posted @ 2022-07-21 19:22  7KByte  阅读(63)  评论(0编辑  收藏  举报