不得不說,這道題目是真的難,真不愧它的「省選/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 edgea[n],b[n];
int head[n],ft[n],ls[n],rs[n],dis[n];
inline lst read()
void cover(rg int a,rg lst c,rg lst j)
void pushdown(rg int a)
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 城池攻占
思路分析 由於這道題的資料範圍是n,m 3e5,所以我們直接輸入乙個模擬乙個是會超時的,但是我們可以在輸入所有的士兵之後把同在乙個節點的士兵一起處理,我們可以考慮建乙個堆,從深度最大的節點開始,維護乙個節點內的士兵的最小戰鬥力值,如果戰鬥力最小的士兵都能存活下來,那麼在堆中的其他士兵一定可以活下來,...
洛谷P3261 JLOI2015 城池攻占
題目大意 有 n 個點的樹,第 i 個節點有乙個權值 h i m 個騎士,第 i 個騎士攻擊力為 v i 乙個騎士可以把從它開始的連續的父親中比它小的節點攻破,攻破乙個節點可以把攻擊力加或乘乙個數 乘的數大於 0 每個騎士獨立 問每個騎士可以攻破多少個點,每個點會阻擋住多少個騎士。題解 可以把所有騎...
P3261 JLOI2015 城池攻占
p3261 jloi2015 城池攻占 乍一看,平衡樹?其實左偏樹更好做啦 qwq 每個節點都來棵左偏樹維護最小值,dfs 往上時合併一下,要是攻不下了就把根節點刪掉,直到能攻下,對了,攻下後值會變化怎麼辦?lazy 標記一下,和線段樹同理 include includeusing namespac...