[洛谷P3261] [JLOI2015]城池攻占

不得不说,这道题目是真的难,真不愧它的“省选/NOI-”的紫色大火题!!!

花了我晚自习前半节课看题解,写代码,又花了我半节晚自习调代码,真的心态爆炸。基本上改得和题解完全一样了我才过了这道题!真的烦。没事,那接下来我来完全把这道题搞透。

Part 1 理解题目

至少我一开始不知道为什么要用左偏树,甚至我看题解一开始也都没弄懂,所以先把题目弄清楚。
首先我们由题可以知道,这要求我们从建好的树的叶子节点开始往上推,有些骑士到特定的点才会出现,check一下骑士能否攻占城池,再记录进答案,更新战斗力,这就很容易想到左偏树可并堆了。

Part 2 解题思想

既然每到一个点会出现一堆的新骑士,所以我们可以在那些点连一些“隐藏边”,到这个点时用链式前向星扫一遍加到一个小根堆中,然后把这个点以下的所有剩下的骑士合并到这个堆中(板子),然后在check时挨个弹出堆顶,如果不能占领就记入答案,能占领我们就要考虑更新骑士,我们不可能直接更新整个堆中的骑士,这样会被硬生生卡成O(n)的修改,所以我们考虑放一个lazy标记在堆顶,每一次合并和删除的时候再下放就可以了。

part 3 code

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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]]

到此为止,顺便膜拜一下大佬zsy,这是他的城池攻占:租酥雨的左偏树城池攻占

你可能感兴趣的:([洛谷P3261] [JLOI2015]城池攻占)