[模板] 各種莫隊
阿新 • • 發佈:2019-04-28
pre 等價 seq math oid 維護 dfs inline 右移
簡介
對於一些區間查詢問題, 當詢問與數組大小同階時, 把詢問按塊排序, 可以得到均攤根號復雜度的算法.
普通莫隊
不含修改, 單點加入/刪除均較快 ( \(O(1)\) 或 \(O(\log n)\) 等).
流程
- 區間大小 \(S = \sqrt n\)
- 排序:
- 按 \((\lceil \frac lS \rceil , r)\) 二元組排序
- 移動左右指針轉移
復雜度 \(O(n\sqrt n)\).
帶修改莫隊
可以單點較快修改.
流程
- 區間大小 \(S = n^{\frac 23}\)
- 排序:
- 將詢問按 \((\lceil\frac l S\rceil, \lceil\frac r S\rceil, t)\)
- 修改不需排序
- 將詢問按 \((\lceil\frac l S\rceil, \lceil\frac r S\rceil, t)\)
- 維護 \(l,r,t\) 三個指針轉移
復雜度 \(O(n^{\frac 53})\).
代碼
struct tq{int l,r,t,id;}q[qsz]; struct tc{int p,v;}c[qsz]; bool cmp(tq l,tq r){return inb[l.l]!=inb[r.l]?inb[l.l]<inb[r.l]:inb[l.r]!=inb[r.r]?inb[l.r]<inb[r.r]:l.t<r.t;} void solp(int p){ //do sth } void solc(int p,int c){ //do sth } void mo(){ sort(q+1,q+pq+1,cmp); int t=0,l=1,r=0; rep(i,1,pq){ while(t<q[i].t)++t,solc(c[t].p,c[t].t); while(t>q[i].t)solc(c[t].p,c[t].v),--t; while(l<q[i].l)solp(seq[l++]); while(l>q[i].l)solp(seq[--l]); while(r<q[i].r)solp(seq[++r]); while(r>q[i].r)solp(seq[r--]); ans[q[i].id]=ans0;//update ans } }
樹上莫隊
序列變成樹, 詢問子樹/鏈信息.
子樹
轉化成dfs序, 然後就變成區間信息了.
鏈
記錄歐拉序, 即每個點入和出各記錄一次,記為in(a), out(a).
考慮樹上的一個鏈 \([a,b]\), 不妨設in(a)<=in(b).
當a, b都不在另一個的子樹中時, 它等價於dfs序中的 \([out(a), in(b)] + lca(a,b)\) ,其中出現過兩次的點不統計;
當b在a的子樹中時, 它等價於dfs序中的 \([in(a), in(b)]\) ,其中出現過兩次的點不統計.
這樣就也轉化為了區間信息, 細節可能有所不同.
或者王室聯邦分塊... 不會
代碼/題目
luogu4074-[WC2013]糖果公園
只增莫隊
有時插入復雜度較小, 但刪除復雜度較大. 考慮只用插入實現.
流程
- 分塊 && 排序, 同普通莫隊.
- 若詢問在一個塊內, 直接暴力;
- 對於其他詢問: 枚舉塊 \([L_s,R_s]\), 處理左端點在該塊內的詢問:
- 起始左指針為 \(R_s + 1\), 右指針為 \(R_s\) ;
- 對於右指針, 根據排序, 同塊內詢問的右端點遞增, 右移指針, 維護加入點即可;
- 對於左指針, 對於每個詢問
- 保存當前的狀態
- 維護左指針向左移動, 加入點
- 詢問完成後恢復左指針到 \(R_s + 1\), 並恢復原來的狀態.
容易發現時間復雜度仍為 \(O(n\sqrt n)\).
[模板] 各種莫隊