gym100299E

题意

给定一棵树,每个点有点权\(a_i\),从根出发,到达给定的一个节点\(t\),初始有血量(hp)为\(0\)第一次经过点\(i\),有\(\text{hp}+a_i\),任意时刻必须满足\(\text{hp}\ge 0\),输出是否能到达\(t\)

做法

考虑顺序遍历一条链,他们的值分别为:\(a_1,a_2,\cdots,a_{k-1},a_k\)
\(b_i=\sum\limits_{k=1}^i a_i\)

我们考虑序列\(\{b_i\}\)的关键值,描述成\([l_1,r_1][l_2,r_2]\cdots[l_m,r_m]\)(满足\(l_i<r_i\)\(r_{i-1}<l_i\)

表示沿着这条链走,会经历\(-l_1+r_1-l_2+r_2\cdots -l_m+r_m\)
这样的实际意义在于\(\sum\limits_{k=1}^{i-1}(l_k+r_k) +l_{i},\sum\limits_{k=1}^i (l_k+r_k)\)是严格的前缀最小值和前缀最大值,\(r_i\)\(l_i\)后的第一个前缀最大值的位置,\(l_i\)\(r_{i-1}\)后的第一个前缀最小值的位置。
当然可能会扔掉序列\(\{b\}\)的某个后缀,这对应于后面这个后缀对答案起不到有效的贡献。

现在考虑两条链的策略
引理:对于两条链\(\{[a_1,a_2],[a_3,a_4],\cdots\}\)\(\{[b_1,b_2],[b_3,b_4],\cdots\}\),第一步走\(a_1,b_1\)较小者是一个不劣的策略。

我们知道了两条链的策略,那么是可以进行合并操作的。
一般的,向二元组序列\(S=\{[l_1,r_1][l_2,r_2]\cdots[l_m,r_m]\}\)插入\([l',r']\)

\(S\)是一堆不交的区间,将\([l',r']\)插入其中,我们查看与其相交的区间,假设为\([l,r]\),顺序合并,根据引理我们已经确定了偏序关系。
那么合并为\(l'\le l\le r'\)\([l',r']+[l,r]=[l',r'+(-l+r)]\)

那么将两个长度为\(m_1,m_2\)的序列可以在\(O((m_1+m_2)\text{log}(m_1+m_2))\)复杂度内合并。

类似的,将\([l',r']\)强制向二元组序列\(S=\{[l_1,r_1][l_2,r_2]\cdots[l_m,r_m]\}\)首部插入也同理。(子树的根)。

利用启发式合并,总复杂度是\(O(nlog^2n)\)的。

code

#include<bits/stdc++.h>
typedef int LL;
typedef double dl;
#define opt operator
#define pb push_back
#define pii std::pair<LL,LL>
const LL maxn=2e5+9,mod=998244353,inf=0x3f3f3f3f;
LL Read(){
	LL x(0),f(1); char c=getchar();
	while(c<'0' || c>'9'){
		if(c=='-') f=-1; c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
	}return x*f;
}
void Chkmin(LL &x,LL y){
	if(y<x) x=y;
}
void Chkmax(LL &x,LL y){
	if(y>x) x=y;
}
LL add(LL x,LL y){
	return x+=y,x>=mod?x-mod:x;
}
LL dec(LL x,LL y){
	return x-=y,x<0?x+mod:x;
}
LL mul(LL x,LL y){
	return 1ll*x*y%mod;
}
LL Pow(LL base,LL b){
	LL ret(1); while(b){
		if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
	}return ret;
}
LL n,t;
LL a[maxn];
std::map<LL,LL> S[maxn];
std::vector<LL> V[maxn];
#define ite std::map<LL,LL>::iterator
void Insert(std::map<LL,LL> &A,pii x){
	ite it1,it2,it3;
	it1=A.find(x.first);
	if(it1==A.end()){
		A.insert(x); it1=A.find(x.first);
	}else{
		it1->second+=x.second;
	}
    if((it2=it1)!=A.begin()){
		--it2;
		if(it1->first<=it2->second){
			it2->second+=-it1->first+it1->second;
			A.erase(it1); it1=it2;
		}
	}
	for(it2=it1,++it2;it2!=A.end();){
		if(it2->first<=it1->second){
			it1->second+=-it2->first+it2->second;
			it3=it2; ++it3;
			A.erase(it2);
			it2=it3;
		}else break;
	}
}
void Merge(std::map<LL,LL> &A,std::map<LL,LL> &B){
	if(A.size()>B.size()) A.swap(B);
	for(ite it1=B.begin();it1!=B.end();++it1){
		Insert(A,pii(it1->first,it1->second));
	}
}
void Modify(std::map<LL,LL> &A,LL x){
	ite it1;
	for(it1=A.begin();it1!=A.end();++it1){
		x+=it1->first;
		if(x<it1->second){
			LL y(it1->second);
			A.erase(A.begin(),++it1);
			A.insert(pii(x,y));
			return;
		}else{
			x-=it1->second;
		}
	}
	A.clear();
}
void Dfs(LL u,LL f){
	if(u==t){
		if(a[u]>=0) S[u].insert(pii(0,inf));
		else S[u].insert(pii(-a[u],inf));
		return;
	}
	for(LL i=0;i<V[u].size();++i){
		LL v(V[u][i]); if(v==f) continue;
		Dfs(v,u);
		Merge(S[u],S[v]);
	}
	if(a[u]>=0){
		Insert(S[u],pii(0,a[u]));
	}else{
		Modify(S[u],-a[u]);
	}
}
void Clear(){
	for(LL i=1;i<=n;++i){
		std::vector<LL>().swap(V[i]);
		S[i].clear();
	}
}
void Solve(){
	n=Read(); t=Read();
	for(LL i=1;i<=n;++i) a[i]=Read();
	for(LL i=1;i<n;++i){
		LL x(Read()),y(Read());
		V[x].pb(y); V[y].pb(x);
	}
	Dfs(1,0);
	LL s(0);
	ite it1;
	LL flag(1);
	for(it1=S[1].begin();it1!=S[1].end();++it1){
		s-=it1->first;
		flag&=(s>=0);
		s+=it1->second;
	}
	if(flag) puts("escaped");
	else puts("trapped");
	Clear();
}
int main(){
	LL T=Read();
	while(T--){
		Solve();
	}
	return 0;
}
posted @ 2021-03-11 13:22  Grice  阅读(99)  评论(0编辑  收藏  举报