不得不说,这道题目是真的难,真不愧它的“省选/NOI-”的紫色大火题!!!
花了我晚自习前半节课看题解,写代码,又花了我半节晚自习调代码,真的心态爆炸。基本上改得和题解完全一样了我才过了这道题!真的烦。没事,那接下来我来完全把这道题搞透。
Part 1 理解题目
至少我一开始不知道为什么要用左偏树,甚至我看题解一开始也都没弄懂,所以先把题目弄清楚。
首先我们由题可以知道,这要求我们从建好的树的叶子节点开始往上推,有些骑士到特定的点才会出现,check一下骑士能否攻占城池,再记录进答案,更新战斗力,这就很容易想到左偏树可并堆了。
Part 2 解题思想
既然每到一个点会出现一堆的新骑士,所以我们可以在那些点连一些“隐藏边”,到这个点时用链式前向星扫一遍加到一个小根堆中,然后把这个点以下的所有剩下的骑士合并到这个堆中(板子),然后在check时挨个弹出堆顶,如果不能占领就记入答案,能占领我们就要考虑更新骑士,我们不可能直接更新整个堆中的骑士,这样会被硬生生卡成O(n)的修改,所以我们考虑放一个lazy标记在堆顶,每一次合并和删除的时候再下放就可以了。
part 3 code
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<ctime>
#include<queue>
#include<stack>
#define lst long long
#define rg register
#define N 300050
using namespace std;
int n,m,cnt;
bool type[N];
int fir[N],deep[N],up[N],dead[N];
lst key[N],def[N],v[N],mul[N],plu[N];
struct edge{
int to,nxt;
}a[N],b[N];
int head[N],ft[N],ls[N],rs[N],dis[N];
inline lst read()
{
rg lst s=0,m=1;rg char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')m=-1,ch=getchar();
while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
return m*s;
}
void cover(rg int A,rg lst c,rg lst j)
{
if(!A)return;
key[A]*=c,key[A]+=j;
mul[A]*=c,plu[A]*=c,plu[A]+=j;
}
void pushdown(rg int A)
{
cover(ls[A],mul[A],plu[A]);
cover(rs[A],mul[A],plu[A]);
mul[A]=1,plu[A]=0;
}
int Merge(rg int A,rg 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 Delete(rg int A)
{
pushdown(A);
return Merge(ls[A],rs[A]);
}
int dfs(rg int now,rg int fm)
{
rg int A=0,B;
deep[now]=deep[fm]+1;
for(rg int i=ft[now];i;i=b[i].nxt)A=Merge(A,b[i].to);
for(rg int i=head[now];i;i=a[i].nxt)
{
B=dfs(a[i].to,now);
A=Merge(A,B);
}
while(key[A]<def[now]&&A)
{
dead[now]++;up[A]=deep[now];
A=Delete(A);
}
if(type[now])cover(A,v[now],0);
else cover(A,1,v[now]);
return A;
}
int main()
{
n=read(),m=read();
for(rg int i=1;i<=n;++i)def[i]=read();
for(rg int i=2;i<=n;++i)
{
rg int go=read();
a[++cnt]=(edge){i,head[go]};head[go]=cnt;
type[i]=read(),v[i]=read();
}cnt=0;
for(rg int i=1;i<=m;++i)
{
key[i]=read(),fir[i]=read();
b[++cnt]=(edge){i,ft[fir[i]]};ft[fir[i]]=cnt;
}
dfs(1,0);
for(rg int i=1;i<=n;++i)printf("%d\n",dead[i]);
for(rg int i=1;i<=m;++i)printf("%d\n",deep[fir[i]]-up[i]);
return 0;
}
到此为止,顺便膜拜一下大佬zsy,这是他的城池攻占:租酥雨的左偏树城池攻占