1. 程式人生 > >BZOJ4003: [JLOI2015]城池攻佔(洛谷P3261)

BZOJ4003: [JLOI2015]城池攻佔(洛谷P3261)

左偏樹

用左偏樹維護騎士生命的最小值。從下往上做,每次對一個城池把生命值小的淘汰掉並更新生命。因為權值可以修改所以要有乘和加的標記。乘和加的兩個操作並不會改變當前堆的結構所以可以進行維護。

程式碼:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define F inline
using namespace std;
typedef long long LL;
struct tree{ int l,r,d; LL v,f1,f2; }t[N];
struct edge{ int nxt,to; }ed[N];
int n,m,k,h[N],a[N],fa[N],c[N],d[N],rt[N],ans[N],dep[N];
LL df[N],w[N];
F char readc(){
	static char buf[100000],*l=buf,*r=buf;
	if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
	return l==r?EOF:*l++;
}
F LL _read(){
	LL x=0,f=1; char ch=readc();
	while (!isdigit(ch)) ch=='-'?f=-1:1,ch=readc();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
	return x*f;
}
F void mdfy(int x,LL f1,LL f2){
	if (!x) return; t[x].v*=f2,t[x].v+=f1;
	t[x].f1*=f2,t[x].f2*=f2,t[x].f1+=f1;
}
F void pshd(int x){ mdfy(t[x].l,t[x].f1,t[x].f2),mdfy(t[x].r,t[x].f1,t[x].f2),t[x].f1=0,t[x].f2=1; }
int mrg(int x,int y){
	if (!x||!y) return x+y; pshd(x),pshd(y);
	if (t[x].v>t[y].v) swap(x,y); t[x].r=mrg(t[x].r,y);
	if (t[t[x].l].d<t[t[x].r].d) swap(t[x].l,t[x].r);
	return t[x].d=t[t[x].r].d+1,x;
}
void dfs(int x){
	dep[x]=dep[fa[x]]+1;
	for (int i=h[x];i;i=ed[i].nxt) dfs(ed[i].to);
	for (int i=h[x];i;i=ed[i].nxt) rt[x]=mrg(rt[x],rt[ed[i].to]);
	while (rt[x]&&t[rt[x]].v<df[x])
		pshd(rt[x]),ans[x]++,d[rt[x]]=x,rt[x]=mrg(t[rt[x]].l,t[rt[x]].r);
	a[x]?mdfy(rt[x],0,w[x]):mdfy(rt[x],w[x],1);
}
#define add(x,y) ed[++k]=(edge){h[x],y},h[x]=k
int main(){
	n=_read(),m=_read();
	for (int i=1;i<=n;i++) df[i]=_read();
	for (int i=2;i<=n;i++)
		fa[i]=_read(),a[i]=_read(),w[i]=_read(),add(fa[i],i);
	for (int i=1;i<=m;i++)
		t[i].v=_read(),c[i]=_read(),t[i].f1=0,t[i].f2=1,rt[c[i]]=mrg(rt[c[i]],i);
	dfs(1); for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
	for (int i=1;i<=m;i++) printf("%d\n",dep[c[i]]-dep[d[i]]);
	return 0;
}