JLOI2015 城池攻占 左偏樹

2022-04-30 01:00:11 字數 2726 閱讀 6031

表示調這道題已經調到失智了。。。 因為昨天剛寫完線段樹2,所以pushdown就寫得和線段樹2一模一樣,,,,於是,,,成功的在pushdown的地方,,,各種錯

下面講做法: 首先我們可以觀察到每個騎士都是獨立的,因此對於每個城池我們可以建乙個堆,堆中維護的是在這個城池的騎士。

維護乙個小根堆,所以如果堆頂的騎士攻擊力不夠的話,就可以直接pop掉,並且給城池死亡人數統計++(dead),這樣用while pop完後,堆裡的騎士就都是可以占領這個城池的了,

由於下面的騎士可以上來,所以這個堆要支援合併,所以我們用左偏樹

因為pop完後剩下的都是合法的,所以這個時候我們就可以對剩下的騎士進行修改了。但是我們觀察到,騎士很多,城池也很多,直接修改顯然不太妥當。於是我們借鑑線段樹的lazy標記,但是由於這裡有乘法也有加法,所以我們維護兩個標記,乙個mul(乘法標記),乙個lazy(加法標記),

由於(x+h)q=xq+hq,所以我們可以把h當做lazy,x當做當前值,q是mul,所以我們更改和下傳乘法標記的時候,要同時把lazy也=mul,

這時lazy==hq,mul==q,因此我們要得到xq+h*q這個結果,我們需要先乘後加

deadin[i]代表騎士i死在**了,dead代表死在這個城池的人數,keep是城市的生命值,其他陣列應該都好懂了

因為我們觀察到如果還要下傳乙個標記won來標記騎士又占領了乙個城池的話顯然是不划算的,因為mul和lazy之所以有必要就是因為過程中要用到,而won標記僅僅是最後要用而已,並且由於騎士的前進路徑是一條鏈,所以我們直接用死亡城市深度-出發城市深度就可以得到攻占幾座城了,如果沒死的話死亡城市就是0,因此為了維護死亡城市-出發城市這個式子的正確性,我們的deep統計從1開始

如果還有不懂的就看**吧,還是有少量注釋的

推薦寫之前先寫線段樹2(luogu3373),有利於理解如何同時維護乘法和加法標記,這裡的標記本質上是一樣的,但是注意不要像我一樣,寫完線段樹2,pushdown就全寫成線段樹版本的,,,,於是就各種錯,,,

下面的輸出格式是為了方便我除錯改的,所以。。。。

寫的可能比較複雜???

表示並不知道為什麼我的**一直比別人長。。。。

-------2018.10.11-------優化了**格式

1 #include2

using

namespace

std;

3#define r register int

4#define ac 300050

5#define ll long long

6#define d printf("line in %d\n",__line__);

7int

n, m;

8int date[ac], next[ac], head[ac], tot, cnt;//

前向星9

ll v[ac], keep[ac];

10int deep[ac], a[ac], dead[ac], deadin[ac];//

城池屬性, 答案

11int

b[ac], root[ac];

1213

inline ll read()

1421

while(c >= '

0' && c <= '

9') x = x * 10 + c - '

0', c =getchar();

22if(!z) return

x;23

else

return -x;24}

2526 inline void add(int f, int

w)27

3031

struct

left_heap

3246

if(lazy[x])

4752}53

54 inline void change_add(int x, int

k)55

5960 inline void change_mul(int x, int

k)61

6566

int merge(int x, int

y)67

7677

void pop(int &x)

7881

82 inline void insert(int x, int

k)83

87}heap;

8889

void dfs(int

x)90

103while(root[x] && heap.s[root[x]]

104109

if(a[x]) heap.change_mul(root[x], v[x]);

110else

heap.change_add(root[x], v[x]);

111}

112113

void

pre()

114124

for(r i = 1; i <= m; i ++)

125136

else

heap.insert(aa, i);

137}

138else dead[b[i]] ++, deadin[i] = b[i];//

不然就別來了

139}

140}

141142

void

work()

143149

150int

main()

151

view code

JLOI2015 左偏樹 城池攻占

題目鏈結 考慮每個節點建乙個以騎士攻擊力為關鍵字的小根堆,從葉子節點向上掃瞄,每次彈堆至堆頂騎士攻擊力大於當前城池防禦力,可以採用左偏樹維護,對於城池的攻擊力改變值,可以借用線段樹區間修改的懶標記思想,打上乘法及加法標記,每次涉及到改變堆結構的操作前下放標記即可。稍微卡常之後可過 include i...

JLOI2015 城池攻占

霧.改變操作乘法是沒有負數的,那麼就不會改變大小關係,我們就可以dfs樹,然後用可合併堆進行操作。splay 啟發式合併也可以過 luogu 3261 bzoj 4003 include include include define int long long const int maxm 3100...

JLOI2015 城池攻占

點此看題 先把每個人放到對應的點上,可以用左偏樹,然後從下往上合併,每次就刪除當前節點會犧牲的人,然後維護乙個 加法 乘法 標記,時間複雜度o n log n o n log n o nlogn include include using namespace std define int long ...