表示調這道題已經調到失智了。。。 因為昨天剛寫完線段樹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 #include2view codeusing
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
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 ...