昨天重溫了一下captainmo的職業生涯 莫隊的模板,看了下別人的部落格,把三個板子打了,做練習前先小小總結了一下吧。
一. 基礎莫隊演算法
莫隊演算法 = 離線 + 暴力 + 分塊,它通常用於不修改只查詢的一類區間問題,複雜度為
主要就是通過排序過後再處理詢問能優化暴力,排序則是利用分塊,至於為什麼更優,附張別人部落格看到的圖。
這就很顯然圖二的走法更短,然後 編碼時,還可以對排序做乙個小優化:奇偶性排序,讓奇數塊和偶數塊的排序相反。例如左端點l都在奇數塊,則對r從大到小排序;若l在偶數塊,則對r從小到大排序(反過來也可以:奇數塊從小到大,偶數塊從大到小)。
luogu p1972 hh的項鍊**,這**不能拿全分(不會卡常的屑 ),但正確性無誤
#include
const
int n=
1e6+10;
using
namespace std;
int n,m,a[n]
,cnt[n]
,ans[n]
,block,id[n]
,t,l,r,sum;
struct ask
q[n]
;inline
intread()
while
(ch>=
'0'&&ch<=
'9')
return f*x;
}bool
comp
(ask a,ask b)
void
add(
int x)
void
del(
int x)
intmain()
sort
(q+1
,q+1
+m,comp)
; l=
1,r=0;
for(
int i=
1;i<=m;i++
)for
(int i=
1;i<=m;i++
)printf
("%d\n"
,ans[i]);
return0;
}
二. 帶修改的莫隊如果是比較簡單的「單點修改」,也能應用莫隊演算法,複雜度
還是有道例題luogu p1903數顏色
如果用莫隊演算法求解,必須離線,先把查詢操作和修改操作分別記錄下來。記錄查詢操作的時候,增加乙個變數,記錄本次查詢前做少次修改。
.如果沒有修改,就是基礎莫隊,乙個查詢的左右端點是[l, r]。加上修改之後,乙個查詢表示為(l, r, t),t表示在查詢[l, r]前進行次修改操作。可以把t理解為「時間」,t的範圍是1 ≤ t ≤ m,m是操作次數。
.反正就是要多考慮修改操作對查詢的影響,又因為是單點修改,可以直接暴力,查詢中就多了這麼一截
void
captainmo()
}
然後就是一些小細節的不同,比如分塊大小應該為因為這樣更快。排序時右端點r也按所在塊的序號大小排,不是r大小。還有就是在修改時要注意寫這句swap(a[x],p[pos].val);
為什麼的話模擬一下就知道了,當時理解了好久才看懂。
**
#include
using
namespace std;
const
int n=
1400000
;int n,m,l,r,x,y,now,id[n]
,block,a[n]
,ans[n]
,cnt[n]
,qnum,pnum,sum;
struct ask
q[n]
;struct upd
p[n]
;inline
intread()
while
(ch>=
'0'&&ch<=
'9')
return f*x;
}bool
comp
(ask a,ask b)
void
add(
int x)
void
del(
int x)
void
update
(int pos,
int i)
swap
(a[x]
,p[pos]
.val);}
void
captainmo()
}int
main()
sort
(q+1
,q+1
+qnum,comp)
;captainmo()
;for
(int i=
1;i<=qnum;i++
)printf
("%d\n"
,ans[i]);
return0;
}
三. 樹上莫隊基礎莫隊和帶修改的莫隊操作的都是一維陣列。基於其他的資料結構的問題,如果能轉換成一維陣列而且是區間問題,那麼也能應用莫隊演算法。
典型的例子是樹形結構上的路徑問題,可以利用「尤拉序」把整棵樹的結點順序轉化為乙個一維陣列,路徑問題也變成了區間問題,就能利用莫隊演算法求解。下面還是有道例題。
sp10707 cot2 - count on a tree ii
相信學過樹剖後對樹上的節點存到佇列已經熟悉,然後就考慮(u, v)上的路徑有哪些結點?首先計算出u、v的lca(u, v)(最近公共祖先),然後討論兩種情況:
(1)lca(u, v) = u或lca(u, v) = v,這種情況最簡單,可以直接做,不多說了。
(2)lca(u, v) ≠ u且lca(u, v) ≠ v,這種情況其實也不複雜,就在查詢時多查詢乙個lca再計算答案,然後再查詢一次消除lca對目前的sum值影響。
至於如何忽略掉區間內出現了兩次的點,這個很簡單,我們多記錄乙個vis[x],表示x這個點有沒有被加入,每次處理的時候如果vis[x]=0則需要新增節點;如果vis[x]=1則需要刪除節點。
**
#include
using
namespace std;
const
int n=
1e5+
100;
int vis[n]
,l,r,cnt[n]
,f[40010][
20],n,m,sum,b[n]
,a[n]
,len,ans[n]
,idx[n]
,idx[n]
,ref[n]
,index_,id[n]
,block,dep[n]
,first[n]
,next[n]
,to[n]
,tot;
struct ask
q[n]
;int
read()
while
(isdigit
(ch)
)return f*x;
}void
add(
int a,
int b)
void
dfs(
int u,
int fa)
idx[u]
=++index_,ref[index_]
=u;}
intlca
(int u,
int v)
bool
comp
(ask a,ask b)
void
update
(int x)
else
}void
captainmo()
}int
main()
dfs(1,
0);// for(int i=1;i<=index_;i++) cout
(int i=
1;i<=m;i++
)else
q[i]
.id=i;
}sort
(q+1
,q+1
+m,comp)
;captainmo()
;for
(int i=
1;i<=m;i++
)printf
("%d\n"
,ans[i]);
return0;
}
該去做練習了啊… 莫隊演算法 入門理解
目前的題型概括為三種 普通莫隊,樹形莫隊以及帶修莫隊。今天主要講的是普通莫隊演算法,莫隊演算法本來就是乙個莽夫演算法,但是他就是對查詢區間排個序後就優化很多了。莫隊演算法巧妙地將詢問離線排序,使得其複雜度無比美妙 在一般做題時我們時常遇到使用排序來優化列舉時間消耗的例子。莫隊的優化基於分塊思想 對於...
莫隊入門總結
這是一篇適合蒟蒻的講解 大佬可以自行離開 莫隊是一種是離線的演算法 即它在詢問的時候是不會修改的,所以我們可以通過調整詢問的次序來獲得答案。比如區間3到5和區間3到6 他們之間只差了1 於是我們只需要看下新加進來的這個元素對原來答案的影響就好了 對吧?問題是 我們應該如何給詢問區間排序使得時間複雜度...
樹上莫隊演算法
繼續回來寫部落格 記錄點有意思的題目什麼的。貌似寫過這個的沒多少人 所以我也記錄一點。首先序列上的莫隊大家都應該很熟悉了 那麼樹上的莫隊要怎麼搞呢?先來看個題目 spoj cot2 求樹上兩點間路徑上有多少個不同的點權。序列上的莫隊是把詢問按照左端點分塊了 可是樹上沒有左端點,怎麼辦呢?我們把樹分塊...