分塊 莫隊學習粗略預習

2022-01-17 07:49:30 字數 4174 閱讀 9377

分塊&莫隊學習總結

2020-08-12 16:55:32

thumb_up

0

大概就是暴力的進化版,採用:「大段維護,小段樸素」的思想

拿個板子說事:

已知乙個數列,你需要進行下面兩種操作:

將某區間每乙個數加上 k。

求出某區間每乙個數的和。

序列長度為1e5,運算元為1e5,裸的線段樹板子

分塊的思路:

將序列劃分成任意大小的塊,注意是任意大小,視題目內容定,總是有些刻板印象硬是要固定塊的大小為根號n,雖然這是最常見的,但是未必是最優的(就比如p4168蒲公英)。分好塊後,可以確定每乙個單點屬於哪個塊(預處理不要程式會慢一點,遇到毒瘤出題人....不可描述)。 接著,塊長為block的話,那麼塊數就是n/block,如果你採用根號n,那麼每次修改的時間複雜度都是根號n,其他的可以計算得出。然後就可以愉快的暴力了

這道模板,對於任意乙個修改操作修改區間 [l,r]

1.l、r均屬於塊p,那麼直接暴力修改,反正塊長為根號n

2.l、r分別屬於p、q,那麼就維護一下塊p+1到q-1,對l到p塊的末尾,暴力修改, 對於r到q塊的開端,暴力修改,中間的區間修改通過標記來處理

對於查詢 ,基本同上

貼上我這因為臉黑 其實是我菜 打70分的分塊**:

#include 

using namespace std;

#define int long long

int n,m,num;

int a[100005],sum[100005],add[100005];

int l[20005],r[20005],pos[100005];

inline int read()

int change(int x,int y,int k);

int getans(int x,int y);

signed main()num=i;

for (int i = 1 ; i <= num ; i++)

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

return 0;

}int getans(int x,int y)

else

}8.19日,我修訂了一下上面原有**,然後a了。。。。

數列分塊入門2:

給出乙個長為 n的數列,以及 n個操作,操作涉及區間加法,詢問區間內小於某個值x 的元素個數。

題外話:

這玩意坑了我乙個上午,woc我看了看題目是求小於,我一直求的是小於等於x的元素個數

思路:首先分析一下題目中的性質,如果a=0都有a+c所以我們的add操作如果覆蓋了一整個塊,那麼其實在這乙個塊內的順序是不會變的,所以我們只用處理旁邊的沒有覆蓋一整塊的區間

也就是零散的區間,就暴力處理,然後新增完對一整個塊重新排序,查詢操作就比較簡單了,因為每乙個塊都是有序的,所以可以在整塊內二分

如果是零散的區間,就直接暴力查詢。

我先給每個塊來個排序(塊內排序),總時間複雜度o(nlogn)

然後要記錄下塊裡面每個塊在原來對應的位置,這個時間複雜度幾乎忽略不計(我可以開結構體啊)

1.對於每乙個查詢,

1.1零散的不足一塊的我就直接暴力查詢有多少個比當前查詢值要小的,

1.2對於整塊的我就二分(因為是排好序的)查詢一下有多少個比當前查詢值小的

2.對於每乙個修改,

2.1零散不足一塊的我就遍歷這個塊,如果這個點的原編號在修改區間內就修改最後重新對這一塊排序就行了

2.2對於滿足一塊的就直接維護add陣列

綜上,總時間複雜度為:o(n*sqrt(n)*log(sqrt(n))),n<=50000,很明顯sqrt(n)不超過240,log(sqrt(n))不超過8,50000*240*8=9.6*10^7,這是最壞情況(也能跑過,如果你rp不那麼低),

也就是n次操作全是查詢而且每次查詢都是1到n,但是實際跑出來的複雜度要遠低於這個值的,所以,大膽暴力!!!

#include using

namespace

std;

#define int long long

int n,m,t=0

;struct noder[200005

];int l[100005],r[100005],block,pos[200005],add[200005

];int cmp(node a,node b)

int change(int x,int y,int

k);int query(int x,int y,int

k);signed main()

for (int i = 1 ; i <= t ; i ++)

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

return0;

}int find(int x,int y,int num,int

ttt)

while(r[x].data + add[ttt] >= num && x>=s)x--;//防止我二分**

if(x > mm)x=mm;

return x-s+1;}

int query(int x,int y,int

k)

else

}int change(int x,int y,int

k)

else

return0;

}

佇列分塊入門三:

這道題和上面的題目基本類似,就是長度為n的序列進行操作,n個操作,每次要求實現區間加法以及查詢【l,r】中x的前驅(即小於x的最大的數)

然後無賴的我把上面的**改了改,然後就過了。。。但是很慢,現在在想怎麼優化一下

貼上(用不要臉的手段)ac的**

#include using

namespace

std;

#define int long long

int n, m, t = 0

;struct

node r[

200005

];int l[100005], r[100005], block, pos[200005], add[200005

];int cmp(node a, node b)

int change(int x, int y, int

k);int query(int x, int y, int

k);signed main()

for (int i = 1; i <= t; i++)

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

return0;

}int find(int x, int y, int num, int

ttt)

while (r[x].data + add[ttt] >= num && x >= s) x--;

if (r[x].data + add[ttt] >=num)

return -1

;

return r[x].data +add[ttt];

}int query(int x, int y, int

k)

else

}int change(int x, int y, int

k)

else

return0;

}

至於正解還在想。

乙個顏色序列,長度為n,現在要統計區間內顏色的總數,給出m個詢問,每次查詢區間【l,r】。 n、m ∈(1,10^5]

運用雙指標法,每次移動指標必然會導致一種顏色減少,一種顏色變多,每次移動都會影響演算法時間複雜度,所以我們要儘量減少移動的次數

那麼, 排序它不香嗎?

問題來了,我們要怎麼排序??

開篇提過,這是個基於分塊的演算法,所以我們還是採用分塊的思想,把每個詢問的l以及r分別屬於哪乙個塊給求出來,按照l屬於的塊作為第一關鍵字排序,r屬於的塊作為第二關鍵字排序,如果兩個詢問的l、r都屬於同乙個塊,再按l作為第三關鍵字,r作為第四關鍵字 (要是還相等那也沒辦法)

然後就可以愉快的暴力了!直接移動ql和qr指標就可以了呀!

bzoj3585 莫隊 分塊

description 有乙個長度為n的陣列。m次詢問,每次詢問乙個區間內最小沒有出現過的自然數。input 第一行n,m。第二行為n個數。從第三行開始,每行乙個詢問l,r。output 一行乙個數,表示每個詢問的答案。sample input 5 52 1 0 2 1 3 32 3 2 41 2 ...

莫隊分塊的幾道題目

1.bzoj彈飛綿羊 跟這道題目一模一樣。hihocode的題目資料比較水,暴力也可以過。分塊,塊內修改 查詢。學習的別人的 1 include2 define pb push back 3 typedef long long ll 4 using namespace std 5 typedef p...

莫隊與分塊精簡小結

我覺得我還要補上帶修莫隊,樹上莫隊等等 先咕著 分塊 引用範圍廣,實現簡潔 注意塊的邊界 一般為 sqrt 分塊。塊中可維護很多東西,維護資訊時從後往前處理。例題 hnoi2010 彈飛綿羊 includeusing namespace std define s x x x inline int r...