[BZOJ4003][JLOI2015]城池攻占

BZOJ
Luogu

sol

补一篇左偏树的题解
骑士对于树上结点挂链,每次合并所有子树上的骑士后把所有攻击力小于城池防御值的骑士弹掉。
左偏树维护加乘懒标记
记得要pushdown
delete的时候也要记得pushdown!

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int MAX=300005;
struct edge{int to,next;}a[MAX],b[MAX];
int head[MAX],cnt,ft[MAX];
bool type[MAX];
ll h[MAX],key[MAX],v[MAX],plu[MAX],mul[MAX];
int n,m,ls[MAX],rs[MAX],dis[MAX],str[MAX],dep[MAX],dead[MAX],up[MAX];
ll gi()
{
	ll x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=-1,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x*w;
}
void cover(int A,ll c,ll j)
{
	if (!A) return;
	key[A]*=c;key[A]+=j;
	mul[A]*=c;plu[A]*=c;plu[A]+=j;
}
void pushdown(int A)
{
	cover(ls[A],mul[A],plu[A]);
	cover(rs[A],mul[A],plu[A]);
	mul[A]=1;plu[A]=0;
}
int merge(int A,int B)
{
	if (!A||!B) return A+B;
	if (key[A]>key[B]) swap(A,B);
	pushdown(A);pushdown(B);
	rs[A]=merge(rs[A],B);
	if (dis[ls[A]]<dis[rs[A]]) swap(ls[A],rs[A]);
	dis[A]=dis[rs[A]]+1;
	return A;
}
int del(int A)
{
	pushdown(A);
	return merge(ls[A],rs[A]);
}
int dfs(int u,int f)
{
	dep[u]=dep[f]+1;
	int A=0,B;
	for (int e=ft[u];e;e=b[e].next) A=merge(A,b[e].to);
	for (int e=head[u];e;e=a[e].next)
	{
		int v=a[e].to;
		B=dfs(v,u);
		A=merge(A,B);
	}
	while (key[A]<h[u]&&A)
	{
		up[A]=dep[u];++dead[u];
		A=del(A);
	}
	if (type[u]) cover(A,v[u],0);
	else cover(A,1,v[u]);
	return A;
}
int main()
{
	n=gi();m=gi();
	for (int i=1;i<=n;i++) h[i]=gi();
	for (int i=2,u;i<=n;i++)
	{
		u=gi();
		a[++cnt]=(edge){i,head[u]};head[u]=cnt;
		type[i]=gi();v[i]=gi();
	}
	cnt=0;
	for (int i=1;i<=m;i++)
	{
		key[i]=gi();str[i]=gi();
		b[++cnt]=(edge){i,ft[str[i]]};ft[str[i]]=cnt;
	}
	dfs(1,0);
	for (int i=1;i<=n;i++) printf("%d\n",dead[i]);
	for (int j=1;j<=m;j++) printf("%d\n",dep[str[j]]-up[j]);
	return 0;
}
posted @ 2018-02-23 12:02  租酥雨  阅读(276)  评论(0编辑  收藏  举报