這是中南第二屆邀請賽的題目,當時還不懂莫隊演算法,,現在做起來感覺思路還是挺清晰的,可以用來當莫隊演算法的練習
首先講下莫隊演算法(也是逆天),是一種分塊的思路(超逆天思維)
它是一種離線演算法,複雜度是o(m*sqrt(n)),m是區間範圍大小,n是區間個數
適用條件:如果知道[l,r]的答案,可以用o(1)或者o(logn)的複雜度求出[l-1,r] [l+1,r] [l,r-1] [l,r+1]的答案,那麼就可以用莫隊演算法(幾乎是區間萬能演算法)
大概的思路是,把n個區間分成sqrt(n)塊,求出區間左端點所在的塊,然後排序
先按左端點所在的塊的大小排序,如果相等,再按照右端點的大小排序。如果右端點的大小相等,再按照左端點的大小排序
然後只要按照排序的順序,依次求出每個區間的答案就可以了
莫隊演算法複雜度證明:
設n為區間個數,m為r最大的值
y向右移動的次數,最極端的情況下,每個塊的y都會從1移動到m,一共有sqrt(n)個塊,所以這的複雜度是m*sqrt(n)
y向左移動的次數,只會在跨塊的時候才會出現,一共只會出現sqrt(n)-1次跨塊的情況,假如每次y都是從n向左移動到了1,所以這的複雜度是m*sqrt(n)
x不跨塊移動的次數,l會在1和這個區間長度的交換,記當前區間長度為w,一共sqrt(n)個塊,乙個塊有sqrt(n)個,複雜度w*sqrt(n)*n=w*n,實際上m平均等於sqrt(m)左右,總的複雜度n*sqrt(m)
x跨塊移動,跨塊只有sqrt(n)-1種情況,每次跨塊最多移動兩個區間長
綜上所述,總的複雜度o(m*sqrt(n))
分塊法有很多逆天的作用,莫隊演算法只是其中的一種用法。
#include#include#include#include#include#include#includeusing namespace std;
const int hs=1000007;
const int mx=100000+5;
typedef long long ll;
int num[mx];
int m,n,unit;
ll a[mx],ans[mx];
int head[hs],next[mx];
void hash_create()
}if(sign)
}} int query(ll x)
return 0;}
void update(ll x,int d)
}} struct query
while(rq[i].r)
ans[q[i].id]=s;
}} int main()
hash_create();
for(int i=1;i<=m;i++)
unit=sqrt(n+0.5);
sort(q+1,q+1+m);
work();
for(int i=1;i<=m;i++)
}return 0;
}
CSU 1515 Sequence (莫隊演算法)
題意 給n個數,m個詢問。每個詢問是乙個區間,求區間內差的絕對值為1的數對數。題解 先離散化,然後莫隊演算法。莫隊是離線演算法,先按按詢問左端點排序,在按右端點排序。ps 第一次寫莫隊,表示挺簡單的,不過這題之前亂搞一氣一直tle,莫隊還是很強大的。include include include i...
樹上莫隊演算法
繼續回來寫部落格 記錄點有意思的題目什麼的。貌似寫過這個的沒多少人 所以我也記錄一點。首先序列上的莫隊大家都應該很熟悉了 那麼樹上的莫隊要怎麼搞呢?先來看個題目 spoj cot2 求樹上兩點間路徑上有多少個不同的點權。序列上的莫隊是把詢問按照左端點分塊了 可是樹上沒有左端點,怎麼辦呢?我們把樹分塊...
模板 莫隊演算法
題意 給定乙個大小為n的陣列,陣列中所有元素的大小 n。你需要回答m個查詢。每個查詢的形式是l,r,k。你需要回答在範圍 l,r 中至少重複k次的數字的個數。n,m 100000 誒,這題卡了好久,tle,中間棄了一段,然後今天學弟學莫隊,拿出這個題,他也沒什麼想法,然後我頓時退一步海闊天空了。最開...