亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

莫隊算法——解決序列上詢問的利器

2019-11-11 04:52:54
字體:
來源:轉載
供稿:網友

問題: 有一個長為N序列,有M個詢問:在區間[L,R]內,出現了多少個不同的數字。(序列中所有數字均小于K)。題目會給出K。

莫隊算法就是滋磁解決這類問題的離線算法。(其實很簡單

首先來看看暴力: 由于暴力還是比較水的,所以直接上:

#include <bits/stdc++.h>using namespace std ;const int maxn = 50010 ;int n, m, a[maxn] ;bool vis[maxn] ;int main() { int i, j, k, query_time, L, R ; cin >> n >> query_time >> k ; for ( i = 1 ; i <= n ; i ++ ) cin >> a[i] ; while ( query_time -- ) { cin >> L >> R ; memset ( vis, 0, sizeof(vis) ) ; for ( i = L ; i <= R ; i ++ ) vis[a[i]] = true ; int ans = 0 ; for ( i = 0 ; i <= k ; i ++ ) ans += vis[i] ; cout << ans << endl ; } return 0 ;}

這個復雜度顯然是 O(N2) 的。有些題目的范圍可能到100000或者更大,那么顯然就不能了。

這里還有一種稍作改進的方法,比上述做法大多數情況要快些。

void add ( int pos ) { ++cnt[a[pos]] ; if ( cnt[a[pos]] == 1 ) ++ answer ;}void remove ( int pos ) { -- cnt[a[pos]] ; if ( cnt[a[pos]] == 0 ) -- answer ;}void solve() { int curL = 1, curR = 0 ; // current L R for ( each query [L,R] ) { while ( curL < L ) remove ( curL++ ) ; while ( curL > L ) add ( --curL ) ; while ( curR < R ) add ( ++curR ) ; while ( curR > R ) remove ( curR-- ) ; cout << answer << endl ; // Warning : please notice the order "--","++" and "cur" ; }}

其實還算好理解的,如果不明白,隨便搞組數據手玩一下就明白了

手玩數據:6 4 31 3 2 1 1 31 42 63 55 6輸出答案:322

不幸的是,雖然這個東西比我們的暴力要快,但是它的時間復雜度仍然是 O(N2) 的。 但好消息是,這個東西可以算是莫隊算法的核心了。(你在逗我笑????) 其實是真的 :)

莫隊算法是怎么做的呢? 考慮到上述改進的辦法的效率低下主要是因為curL和curR兩個指針前前后后跑來跑去太多次。于是,我們的做法就是不要讓他們跑太多沒用的距離。 我們可以通過離線下所有的詢問,然后通過某種排序,讓兩個指針跑動的距離盡量變少。具體的做法是把N劃分成N??√段,每段長度都是N??√,然后在把所有詢問按照L端點排序,看各個詢問被劃分到哪一塊里。接著,對于各個劃分出的段,在各自的段里,將它包含的所有區間再按照R端點排序。 舉個例子:假設我們有3個長度為3的段(0-2,3-5,6-8): {0, 3} {1, 7} {2, 8} {7, 8} {4, 8} {4, 4} {1, 2} 先根據所在段落的編號重排 {0, 3} {1, 7} {2, 8} {1, 2} | {4, 8} {4, 4} | {7, 8} 現在按R的值重排 {1, 2} {0, 3} {1, 7} {2, 8} | {4, 4} {4, 8} | {7, 8}

然后?還要然后嗎?就用剛剛那個算法來玩就好了。畢竟我們只是交換了一下詢問的順序而已,并沒有對算法做什么改動。

對于這個的復雜度嘛,其實是比較鬼畜的,就這么改了下順序,然后就變成了O(N?N??√)

上面代碼所有查詢的復雜性是由4個while循環決定的。前2個while循環是curL的移動總量”,后2個while循環是curR的移動總量”。這兩者的和將是總復雜度。 有趣的是。先考慮右指針:對于每個塊,查詢是遞增的順序排序,所以右指針curR按照遞增的順序移動。在下一個塊的開始時,指針是盡量靠右的 ,將移動到下一個塊中的最小的R處。這意味著對于一個給定的塊,右指針移動的量是 O(N)。我們有O(N??√)個塊。所以總共是O(N?N??√)。 關于左指針:所有查詢的左指針都在同一段中,當我們完成一個查詢到下一個查詢時,左指針會移動,但由于兩次詢問的L在同一塊中,此移動是 O(N??√)的。所以,左指針的移動總量是O(M?N??√)。 所以,總復雜度就是O((N+M)?N??√),就當做是O(N?N??√)吧。 是不是很簡單的吶2333

接下來給幾個例題:

例題1:BZOJ3781 小B的詢問

Description

小B有一個序列,包含N個1~K之間的整數。他一共有M個詢問,每個詢問給定一個區間[L..R],求Sigma(c(i)^2)的值,其中i的值從1到K,其中c(i)表示數字i在[L..R]中的重復次數。小B請你幫助他回答詢問。

Input

第一行,三個整數N、M、K。 第二行,N個整數,表示小B的序列。 接下來的M行,每行兩個整數L、R。

Output

M行,每行一個整數,其中第i行的整數表示第i個詢問的答案。

Sample Input

6 4 3 1 3 2 1 1 3 1 4 2 6 3 5 5 6

Sample Output

6 9 5 2

HINT

對于全部的數據,1<=N、M、K<=50000

很裸的吧~

/************************************************************** Source Code : GoAway Date : 2017-02-06****************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <vector>#include <map>#include <stack>#include <queue>#include <set>#include <cmath>#include <algorithm>#include <ctime>using namespace std ;const int zhf = 1<<30 ;const int maxn = 50010, tim = 250 ;bool Read ( int &x ) { bool f = 0 ; x = 0 ; char c = getchar() ; while ( !isdigit(c) ) { if ( c == '-' ) f = 1 ; if ( c == EOF ) return false ; c = getchar() ; } while ( isdigit(c) ) { x = 10 * x + c - '0' ; c = getchar() ; } if ( f ) x = -x ; return true ;}struct query { int L, R, id ; friend bool Operator < ( query a, query b ) { return (a.L/tim) == (b.L/tim) ? a.R < b.R : a.L < b.L ; }} e[maxn] ;int n, m, a[maxn], cnt[maxn], ans[maxn], answer ;void add ( int pos ) { answer += (cnt[a[pos]]++)<<1|1 ;}void remove ( int pos ) { answer -= (--cnt[a[pos]])<<1|1 ;}int main() { int i, j, k, curL = 1, curR = 0 ; Read(n) ; Read(m) ; Read(j) ; for ( i = 1 ; i <= n ; i ++ ) Read(a[i]) ; for ( i = 1 ; i <= m ; i ++ ) { Read(e[i].L) ; Read(e[i].R) ; e[i].id = i ; } sort ( e+1, e+m+1 ) ; for ( i = 1 ; i <= m ; i ++ ) { int L = e[i].L, R = e[i].R ; while ( curL < L ) remove ( curL++ ) ; while ( curL > L ) add ( --curL ) ; while ( curR < R ) add ( ++curR ) ; while ( curR > R ) remove ( curR-- ) ; ans[e[i].id] = answer ; } for ( i = 1 ; i <= m ; i ++ ) PRintf ( "%d/n", ans[i] ) ; return 0 ;}

例題2:SDOI2009 HH的項鏈 洛谷1972

Description

HH有一串由各種漂亮的貝殼組成的項鏈。HH相信不同的貝殼會帶來好運,所以每次散步 完后,他都會隨意取出一段貝殼,思考它們所表達的含義。HH不斷地收集新的貝殼,因此, 他的項鏈變得越來越長。有一天,他突然提出了一個問題:某一段貝殼中,包含了多少種不同 的貝殼?這個問題很難回答。。。因為項鏈實在是太長了。于是,他只好求助睿智的你,來解 決這個問題。

Input

第一行:一個整數N,表示項鏈的長度。 第二行:N個整數,表示依次表示項鏈中貝殼的編號(編號為0到1000000之間的整數)。 第三行:一個整數M,表示HH詢問的個數。 接下來M行:每行兩個整數,L和R(1 ≤ L ≤ R ≤ N),表示詢問的區間。

Output

M行,每行一個整數,依次表示詢問對應的答案。

Sample Input

6 1 2 3 4 3 5 3 1 2 3 5 2 6

Sample Output

2 2 4

HINT

對于20%的數據,N ≤ 100,M ≤ 1000; 對于40%的數據,N ≤ 3000,M ≤ 200000; 對于100%的數據,N ≤ 50000,M ≤ 200000。

還是很裸的2333

/************************************************************** Source Code : GoAway Date : 2017-02-06****************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <vector>#include <map>#include <stack>#include <queue>#include <set>#include <cmath>#include <algorithm>#include <ctime>using namespace std ;const int zhf = 1<<30 ;const int maxn = 1000010 ;bool Read ( int &x ) { bool f = 0 ; x = 0 ; char c = getchar() ; while ( !isdigit(c) ) { if ( c == '-' ) f = 1 ; if ( c == EOF ) return false ; c = getchar() ; } while ( isdigit(c) ) { x = 10 * x + c - '0' ; c = getchar() ; } if ( f ) x = -x ; return true ;}int n, m, cnt[maxn], a[maxn], answer, ans[maxn], tim ;struct query { int l, r, id ; friend bool operator < ( query a, query b ) { return (a.l/tim) == (b.l/tim) ? a.r < b.r : a.l<b.l ; }} e[maxn] ;void add ( int pos ) { if ( (++cnt[a[pos]]) == 1 ) ++ answer ;}void remove ( int pos ) { if ( (--cnt[a[pos]]) == 0 ) -- answer ;}int main() { int i, j, k, curL = 1, curR = 0 ; Read(n) ; for ( i = 1 ; i <= n ; i ++ ) Read(a[i]) ; Read(m) ; tim = sqrt(m) ; for ( i = 1 ; i <= m ; i ++ ) { Read(e[i].l) ; Read(e[i].r) ; e[i].id = i ; } sort(e+1,e+m+1) ; for ( i = 1 ; i <= m ; i ++ ) { int L = e[i].l, R = e[i].r ; while ( curL < L ) remove(curL++) ; while ( curL > L ) add(--curL) ; while ( curR < R ) add(++curR) ; while ( curR > R ) remove(curR--) ; ans[e[i].id] = answer ; } for ( i = 1 ; i <= m ; i ++ ) printf ( "%d/n", ans[i] ) ; return 0 ;}

例題3:2009國家集訓隊 小Z的襪子 清橙OJ1206

Description

HH有一串由各種漂亮的貝殼組成的項鏈。HH相信不同的貝殼會帶來好運,所以每次散步 完后,他都會隨意取出一段貝殼,思考它們所表達的含義。HH不斷地收集新的貝殼,因此, 他的項鏈變得越來越長。有一天,他突然提出了一個問題:某一段貝殼中,包含了多少種不同 的貝殼?這個問題很難回答。。。因為項鏈實在是太長了。于是,他只好求助睿智的你,來解 決這個問題。

Input

第一行:一個整數N,表示項鏈的長度。 第二行:N個整數,表示依次表示項鏈中貝殼的編號(編號為0到1000000之間的整數)。 第三行:一個整數M,表示HH詢問的個數。 接下來M行:每行兩個整數,L和R(1 ≤ L ≤ R ≤ N),表示詢問的區間。

Output

M行,每行一個整數,依次表示詢問對應的答案。

Sample Input

6 1 2 3 4 3 5 3 1 2 3 5 2 6

Sample Output

2 2 4

HINT

對于20%的數據,N ≤ 100,M ≤ 1000; 對于40%的數據,N ≤ 3000,M ≤ 200000; 對于100%的數據,N ≤ 50000,M ≤ 200000。

樣例解釋

詢問1:共C(5,2)=10種可能,其中抽出兩個2有1種可能,抽出兩個3有3種可能,概率為(1+3)/10=4/10=2/5。 詢問2:共C(3,2)=3種可能,無法抽到顏色相同的襪子,概率為0/3=0/1。 詢問3:共C(3,2)=3種可能,均為抽出兩個3,概率為3/3=1/1。 注:上述C(a, b)表示組合數,組合數C(a, b)等價于在a個不同的物品中選取b個的選取方案數。

這道題倒是有些需要想想。 但是有一個神奇的事情:C(N,M)=N!M!?(N?M)! 那么C(N,2)呢? 這不就是一個∑N?1i=1i嘛? (有沒有感覺自己被續了一秒

/************************************************************** Source Code : GoAway Date : 2017-02-06****************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <vector>#include <map>#include <stack>#include <queue>#include <set>#include <cmath>#include <algorithm>#include <ctime>#define ll long longusing namespace std ;const ll zhf = 1<<30 ;const ll maxn = 50010 ;bool Read ( ll &x ) { bool f = 0 ; x = 0 ; char c = getchar() ; while ( !isdigit(c) ) { if ( c == '-' ) f = 1 ; if ( c == EOF ) return false ; c = getchar() ; } while ( isdigit(c) ) { x = 10 * x + c - '0' ; c = getchar() ; } if ( f ) x = -x ; return true ;}ll tim ;struct query { ll L, R, id ; friend bool operator < ( query a, query b ) { return (a.L/tim) == (b.L/tim) ? a.R < b.R : a.L < b.L ; }} e[maxn] ;ll gcd ( ll x, ll y ) { return y ? gcd ( y, x%y ) : x ;}struct Answer { ll x, y ; void out() { if ( !x ) puts("0/1") ; else { ll d = gcd(x, y) ; x /= d ; y /= d ; printf ( "%lld/%lld/n", x, y ) ; } }} ans[maxn] ;ll n, m, a[maxn], cnt[maxn], answer ;void add ( ll pos ) { ++ cnt[a[pos]] ; if ( cnt[a[pos]] > 1 ) answer += cnt[a[pos]] - 1 ;}void remove ( ll pos ) { -- cnt[a[pos]] ; if ( cnt[a[pos]] > 0 ) answer -= cnt[a[pos]] ;}ll sum ( ll x ) { return x*(x+1)/2 ; }int main() { ll i, j, k, curL = 1, curR = 0 ; Read(n) ; Read(m) ; tim = sqrt(m) ; for ( i = 1 ; i <= n ; i ++ ) Read(a[i]) ; for ( i = 1 ; i <= m ; i ++ ) { Read(e[i].L) ; Read(e[i].R) ; e[i].id = i ; } sort ( e+1, e+m+1 ) ; for ( i = 1 ; i <= m ; i ++ ) { ll L = e[i].L, R = e[i].R ; while ( curL < L ) remove ( curL++ ) ; while ( curL > L ) add ( --curL ) ; while ( curR < R ) add ( ++curR ) ; while ( curR > R ) remove ( curR-- ) ; ans[e[i].id] = (Answer){answer,sum(R-L)} ; } for ( i = 1 ; i <= m ; i ++ ) ans[i].out() ; return 0 ;}

其實莫隊還可以套上樹狀數組或者在一棵樹上搞的,但是這篇文章就簡單先介紹下啦~ 要是俺有空就出莫隊的續集,嘿嘿~


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩精品在线播放| 国产婷婷色综合av蜜臀av| 欧美另类极品videosbest最新版本| 精品国产精品三级精品av网址| 亚洲免费人成在线视频观看| 国产欧美亚洲视频| 成人精品福利视频| 国产亚洲精品成人av久久ww| 奇门遁甲1982国语版免费观看高清| 色综合久久中文字幕综合网小说| 国产一区二区av| 国产激情综合五月久久| 国产视频自拍一区| 欧美极品美女电影一区| 欧美性xxxxxxxxx| 91久久精品国产91久久| 亚洲精品国产精品自产a区红杏吧| 欧美激情综合色综合啪啪五月| 97碰碰碰免费色视频| 91免费看片网站| 日韩成人中文字幕在线观看| 欧美日本黄视频| 亚洲精品一区二区在线| 国产999精品久久久| 在线精品高清中文字幕| 亚洲精品欧美日韩专区| 国产精品精品久久久久久| 欧洲中文字幕国产精品| 91夜夜未满十八勿入爽爽影院| 日韩电影大全免费观看2023年上| 亚洲一区二区在线播放| 日本成人激情视频| 精品人伦一区二区三区蜜桃网站| 日韩成人网免费视频| 日本中文字幕不卡免费| 精品无码久久久久久国产| 国产欧美一区二区三区四区| 欧美性猛交xxxx免费看久久久| 91欧美精品成人综合在线观看| 91精品久久久久久久久| 亚洲国产欧美一区| 成人黄色免费在线观看| 中文字幕在线成人| 国内精品模特av私拍在线观看| 欧美激情在线有限公司| 亚洲无线码在线一区观看| 欧美性生交xxxxx久久久| 日韩国产中文字幕| 国产精品欧美日韩一区二区| 欧美极品少妇xxxxⅹ裸体艺术| 久久99精品国产99久久6尤物| 亚洲成成品网站| 国产精品久久久久久久久借妻| 亚洲精品中文字| 欧美精品一区二区免费| 国产成人精品免高潮在线观看| 8090理伦午夜在线电影| 国产精品午夜视频| 日本最新高清不卡中文字幕| 日韩美女视频免费在线观看| 亚洲第一免费网站| 久久成人综合视频| 18久久久久久| 国产成人精品午夜| 亚洲精品综合精品自拍| 国产精品va在线播放我和闺蜜| 精品亚洲夜色av98在线观看| 欧美高跟鞋交xxxxxhd| 久久久免费精品| 日本午夜在线亚洲.国产| www.国产一区| 国产精品18久久久久久麻辣| 亚洲xxx视频| 黄色成人在线免费| 国产亚洲欧美aaaa| 国产精品小说在线| 亚洲2020天天堂在线观看| 另类美女黄大片| 国产精品久久久久久久美男| 亚洲美女精品成人在线视频| 久久久精品中文字幕| 亚洲图片在区色| 91久久夜色精品国产网站| 久久免费视频网站| 欧美亚洲国产成人精品| 韩国视频理论视频久久| 精品国产一区av| 91亚洲精品一区| 奇米一区二区三区四区久久| 2019亚洲日韩新视频| 91网站免费观看| 中文字幕国内精品| 欧美日韩性视频| 日韩高清av在线| 少妇高潮久久久久久潘金莲| 欧美性videos高清精品| 国产精品中文久久久久久久| 欧美片一区二区三区| 国产一区二区美女视频| 国产成人精品日本亚洲专区61| 在线观看久久久久久| 日韩精品亚洲元码| 热门国产精品亚洲第一区在线| 亚洲国产精品人久久电影| 久久精品在线播放| 欧美成人剧情片在线观看| 国产成人欧美在线观看| 国产一区二区三区丝袜| 久久久电影免费观看完整版| 日本免费在线精品| 亚洲免费av电影| 亚洲欧洲日本专区| 欧美精品在线极品| 亚洲第一男人天堂| 欧美福利视频网站| 亚洲一区二区三区视频播放| 亚洲欧美日韩一区二区三区在线| 国产精品99久久久久久久久久久久| 中文字幕精品影院| 国产91亚洲精品| 亚洲加勒比久久88色综合| 欧美xxxwww| 久久91亚洲精品中文字幕奶水| 国产福利成人在线| 在线观看国产欧美| 91在线直播亚洲| 国产精品视频区| 精品日韩美女的视频高清| 98午夜经典影视| 国产精品爱啪在线线免费观看| 久久久av网站| 欧美一级视频一区二区| 亚洲国产精品久久精品怡红院| 成人精品在线视频| 成人精品一区二区三区电影免费| 欧美丰满少妇xxxxx做受| 日韩av高清不卡| 久久久伊人欧美| 91理论片午午论夜理片久久| 久久久人成影片一区二区三区| 久久久精品免费| 国产国语刺激对白av不卡| 亚洲国语精品自产拍在线观看| 国产噜噜噜噜久久久久久久久| 亲子乱一区二区三区电影| 成人福利网站在线观看11| 亚洲第一色在线| 韩国日本不卡在线| 亚洲欧洲美洲在线综合| 欧美激情亚洲精品| 亚洲国产精品人久久电影| 国产日本欧美视频| 日本最新高清不卡中文字幕| 亚洲在线一区二区| 国产精品久久久久久久7电影| 亚洲片在线资源| 久久综合免费视频| 久久香蕉精品香蕉| 成人午夜一级二级三级| 欧美影院久久久| 亚洲一区二区三区在线免费观看| 亚洲国产精品国自产拍av秋霞| 亚洲欧美成人在线| 色www亚洲国产张柏芝|