[JLOI2015]城池攻占

左偏樹的題。
把每個節點上有的騎士按照攻擊力的大小建一個小根堆。dfs的時候把兒子們的都合並過來,看看update完了的值是否小於防禦值。小於的話就pop,然後ans[x]++。記得開long long

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long
const int N=300005;
int n,m,h[N],s[N],c[N],tagx[N],tagj[N],nxt[N],to[N],a[N],v[N],head[N],ecnt,ls[N],rs[N],val[N],dis[N],ans[N],fa[N],dep[N],rt[N];
inline void add(int bg,int ed) {nxt[++ecnt]=head[bg];to[ecnt]=ed;head[bg]=ecnt;}
void update(int x,int mul,int ad) {
	if(!x) return;
	val[x]*=mul,val[x]+=ad,tagj[x]*=mul,tagj[x]+=ad,tagx[x]*=mul;
}
inline void pushdown(int x){update(ls[x],tagx[x],tagj[x]),update(rs[x],tagx[x],tagj[x]),tagx[x]=1,tagj[x]=0;}
int merge(int x,int y) {
	if(!(x*y)) return x+y;
	if(val[x]>val[y]) swap(x,y);
	pushdown(x);
	rs[x]=merge(rs[x],y);
	if(dis[rs[x]]>dis[ls[x]])swap(ls[x],rs[x]);
	dis[x]=dis[rs[x]]+1;
	return x;
}
int pos[N];
void dfs(int x) {
	for(int i=head[x];i;i=nxt[i]) {
		dep[to[i]]=dep[x]+1;
		dfs(to[i]);
		rt[x]=merge(rt[x],rt[to[i]]);
	}
	while(rt[x]&&val[rt[x]]<h[x]) {
		
		ans[x]++;pushdown(rt[x]);pos[rt[x]]=x;rt[x]=merge(ls[rt[x]],rs[rt[x]]);
	}
	if(a[x]) update(rt[x],v[x],0);
	else update(rt[x],1,v[x]);
}
main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&h[i]);
	for(int i=2,x;i<=n;i++) scanf("%lld%lld%lld",&fa[i],&a[i],&v[i]),add(fa[i],i);
	for(int i=1;i<=m;i++) {scanf("%lld%lld",&val[i],&c[i]);tagx[i]=1;rt[c[i]]=merge(rt[c[i]],i);}
	dep[1]=1;
	dfs(1);
	for(int i=1;i<=n;i++) printf("%lld
",ans[i]);
	for(int i=1;i<=n;i++) printf("%lld
",dep[c[i]]-dep[pos[i]]);
	return 0;
}
我是咸鱼。转载博客请征得博主同意Orz
原文地址:https://www.cnblogs.com/sdfzhsz/p/9629947.html