Gym 100299E BZOJ 4054 [CERC2013]Escape (启发式合并)

题目链接

(Gym) https://codeforces.com/gym/100299
(BZOJ) 大人,时代变了。

题解

一个显然的思路是树形 DP,设 (f[u]) 为一个二元组的集合,每个二元组 ((x,y)) 表示“如果我有至少 (x) 的血量,那么我可以多得到 (y) 的血量”。显然看成区间 ([x,x+y]) 后不交。
一个思路是使用 set 维护这些二元组,每次启发式合并,把小的集合里的每个元素暴力插入大的集合;对于权值是负的节点 (u) 使用整体打标记的方法给所有的 (x) 都加上 (-w_u). 一个元素的插入可能导致集合中多个元素的合并,但因为一共只有 (O(nlog n)) 个元素,每次合并都会少一个元素,通过势能分析可知总共只会合并 (O(nlog n)) 次,复杂度是 (O(nlog^2 n))
但是这样代码难度想想就恐怖……上 dark 看到了一份不到 1kb 的神仙代码,发现他用的是堆。
于是脑补了如下做法:我们发现其实没有必要实时保证所有的 ([x,y]) 不交,只需要在每次把子树集合合并完后加入 (u) 号节点的时候,如果 (w_u) 是负的就从小到大进行一次合并,直到合并成正的为止。(详见代码)
时间复杂度 (O(nlog^2n)),空间复杂度 (O(nlog n)).
(突然意识到为什么我写了一个堆的启发式合并而没有想到可并堆这个东西/fad)

代码

#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define x first
#define y second
#define iter iterator
#define riter reverse_iterator
#define y1 Lorem_ipsum_
#define tm dolor_sit_amet_
#define pll pair<llong,llong>
using namespace std;

inline int read()
{
	int x = 0,f = 1; char ch = getchar();
	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
	return x*f;
}

const int mxN = 2e5;
const llong INF = 1e12;
int n,t,en;
struct Edge {int v,nxt;} e[mxN*2+3]; int fe[mxN+3]; int fa[mxN+3];
llong a[mxN+3];
priority_queue<pll,vector<pll>,greater<pll> > q[mxN+3];

void addedge(int u,int v)
{
	en++; e[en].v = v;
	e[en].nxt = fe[u]; fe[u] = en;
}

void dfs(int u)
{
	while(!q[u].empty()) {q[u].pop();}
	for(int i=fe[u]; i; i=e[i].nxt)
	{
		int v = e[i].v; if(v==fa[u]) continue; fa[v] = u;
		dfs(v);
		if(q[v].size()>q[u].size()) {swap(q[u],q[v]);}
		while(q[v].size()) {q[u].push(q[v].top()); q[v].pop();}
	}
	pll x; if(a[u]>=0ll) {x = mkpr(0ll,a[u]);} else {x = mkpr(-a[u],a[u]);}
	while(!q[u].empty()&&(x.y<0ll||q[u].top().x<=x.x+x.y)) {pll y = q[u].top(); q[u].pop(); x = mkpr(max(x.x,y.x-x.y),x.y+y.y);}
	if(x.y>=0ll) {q[u].push(x);}
}

int main()
{
	int T = read(); while(T--)
	{
		n = read(),t = read();
		for(int i=1; i<=n; i++) a[i] = read();
		for(int i=1; i<n; i++) {int u = read(),v = read(); addedge(u,v); addedge(v,u);}
		n++; addedge(t,n); a[n] = INF;
		dfs(1);
		if(!q[1].empty()&&q[1].top().x==0ll&&q[1].top().y>=INF/2ll) {puts("escaped");} else {puts("trapped");}
		for(int i=1; i<=n; i++) fe[i] = fa[i] = 0;
		for(int i=1; i<=en; i++) e[i].v = e[i].nxt = 0;
		en = 0;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/suncongbo/p/14270921.html