[CERC2013] Escape

一、题目

点此看题

二、解法

显然的思路是计算 \(f(u,i)\) 表示进入子树 \(u\) 时的生命值为 \(i\),最多赚取的生命值是多少。我们可以在终点 \(t\) 下面连接一个大小为 \(+\infty\) 的点,那么判断能否到 \(T\) 可以化归到判断 \(f(1,0)\geq +\infty\) 是否成立。

但是这东西并不好转移,因为你可能在生命值变化后反复进入一个子树。关键的 \(\tt observation\) 是:\(f(u,i)\) 关于 \(i\) 单调不降。那么我们可以转而维护 \(f(u,i)\) 的差分值,也就是对点 \(u\) 维护若干个二元组 \((x,y)\),表示如果生命值 \(\geq x\),就可以通过该子树额外赚取 \(y\) 的生命值。

发现子树的二元组是可以直接合并的,我们只需要考虑添加点 \(u\) 的影响,\(a[u]\geq 0\) 是平凡的,直接添加二元组 \((0,a[u])\)

如果 \(a[u]<0\),因为要维护赚取的性质所以不能直接添加。考虑要加入的二元组是 \((A,B)\),初始时 \(A=-a[u],B=a[u]\),我们取出 \(x\) 最小的 \((x,y)\),如果满足 \(x\leq A\) 或者 \(B<0\),那么把这两个二元组合并:

\[A\leftarrow \max(A,x-B),B\leftarrow B+y \]

说明:考虑第一阶段的合并是因为 \(B<0\),由于我们进入这个子树是来赚钱的,所以要让 \(B>0\),赚钱的门槛也会随之提高(\(A\) 会变化,因为会新增条件 \(HP+B\geq x\));考虑第二阶段的合并是因为 \(x\leq A\),因为赚钱至少都要 \(A\),否则是赚不到钱的,那么要把门槛低的一些二元组给合并上来。

最后计算答案的时候,取出 \(x\) 最小的 \((x,y)\),如果 \(x\leq HP\) 就让 \(HP\leftarrow HP+y\),一直循环这个过程即可。

由于每次只会取出 \(x\) 最小的二元组,可以使用左偏树维护,时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 200005;
#define int long long
const int inf = 1e18;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int T,n,t,a[M],x[M],y[M],ls[M],rs[M],rt[M],d[M];
vector<int> g[M];
int merge(int u,int v)
{
	if(!u || !v) return u+v;
	if(x[u]>x[v]) swap(u,v);
	rs[u]=merge(rs[u],v);
	if(d[rs[u]]>d[ls[u]]) swap(ls[u],rs[u]);
	d[u]=d[rs[u]]+1;
	return u;
}
void dfs(int u,int fa)
{
	for(int v:g[u]) if(v^fa)
	{
		dfs(v,u);
		rt[u]=merge(rt[u],rt[v]);
	}
	if(a[u]>0)
	{
		x[u]=0;y[u]=a[u];d[u]=1;
		rt[u]=merge(rt[u],u);
	}
	else
	{
		int A=-a[u],B=a[u];
		while(rt[u] && (x[rt[u]]<=A || B<0))
		{
			A=max(A,x[rt[u]]-B);
			B+=y[rt[u]];
			rt[u]=merge(ls[rt[u]],rs[rt[u]]);
		}
		if(B>0)
		{
			x[u]=A;y[u]=B;d[u]=1;
			rt[u]=merge(rt[u],u);
		}
	}
}
void work()
{
	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();
		g[u].push_back(v);
		g[v].push_back(u);
	}
	g[++n].push_back(t);
	g[t].push_back(n);a[n]=inf;
	dfs(1,0);t=0;
	while(rt[1] && x[rt[1]]<=t)
	{
		t+=y[rt[1]];
		rt[1]=merge(ls[rt[1]],rs[rt[1]]);
	}
	if(t>=inf) puts("escaped");
	else puts("trapped");
	for(int i=1;i<=n;i++)
	{
		g[i].clear();
		x[i]=y[i]=ls[i]=rs[i]=rt[i]=d[i]=0;
	}
}
signed main()
{
	T=read();
	while(T--) work();
}
posted @ 2022-06-08 16:42  C202044zxy  阅读(136)  评论(3编辑  收藏  举报