1. 程式人生 > >[洛谷P3261] [JLOI2015]城池攻占(左偏樹)

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

戰鬥力 man HA swa pan 省選 deep AD com

不得不說,這道題目是真的難,真不愧它的“省選/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]城池攻占(左偏樹)