[洛谷P3261] [JLOI2015]城池攻占(左偏树)

Posted eternal风度

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[洛谷P3261] [JLOI2015]城池攻占(左偏树)相关的知识,希望对你有一定的参考价值。

不得不说,这道题目是真的难,真不愧它的“省选/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,这是他的城池攻占:666

以上是关于[洛谷P3261] [JLOI2015]城池攻占(左偏树)的主要内容,如果未能解决你的问题,请参考以下文章

P3261 [JLOI2015]城池攻占 [贪心,左偏树]

BZOJ4003[JLOI2015]城池攻占 可并堆

BZOJ 4003 JLOI2015城池攻占

BZOJ4003JLOI2015城池攻占

JLOI 2015城池攻占

bzoj4003[JLOI2015]城池攻占 可并堆