• [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();
    }
    
  • 相关阅读:
    Python导入运行的当前模块报错
    关于相对性的思考
    B+树
    DNS:从零搭建公司内网DNS服务器
    【05】Saltstack:配置详解
    【04】Saltstack:配置管理
    【03】Saltstack:远程执行
    【02】Saltstack:Grains and Pillar
    【01】Saltstack:从零开始 Saltstack
    【08】Kubernets:Service
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16356234.html
Copyright © 2020-2023  润新知