莫隊演算法入門

2021-10-09 14:24:28 字數 4316 閱讀 2538

昨天重溫了一下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 求樹上兩點間路徑上有多少個不同的點權。序列上的莫隊是把詢問按照左端點分塊了 可是樹上沒有左端點,怎麼辦呢?我們把樹分塊...