問題: 有一個長為N序列,有M個詢問:在區(qū)間[L,R]內(nèi),出現(xiàn)了多少個不同的數(shù)字。(序列中所有數(shù)字均小于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 ;}這個復(fù)雜度顯然是
這里還有一種稍作改進的方法,比上述做法大多數(shù)情況要快些。
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" ; }}其實還算好理解的,如果不明白,隨便搞組數(shù)據(jù)手玩一下就明白了
手玩數(shù)據(jù):6 4 31 3 2 1 1 31 42 63 55 6輸出答案:322不幸的是,雖然這個東西比我們的暴力要快,但是它的時間復(fù)雜度仍然是
莫隊算法是怎么做的呢? 考慮到上述改進的辦法的效率低下主要是因為curL和curR兩個指針前前后后跑來跑去太多次。于是,我們的做法就是不要讓他們跑太多沒用的距離。 我們可以通過離線下所有的詢問,然后通過某種排序,讓兩個指針跑動的距離盡量變少。具體的做法是把N劃分成
然后?還要然后嗎?就用剛剛那個算法來玩就好了。畢竟我們只是交換了一下詢問的順序而已,并沒有對算法做什么改動。
對于這個的復(fù)雜度嘛,其實是比較鬼畜的,就這么改了下順序,然后就變成了
上面代碼所有查詢的復(fù)雜性是由4個while循環(huán)決定的。前2個while循環(huán)是curL的移動總量”,后2個while循環(huán)是curR的移動總量”。這兩者的和將是總復(fù)雜度。 有趣的是。先考慮右指針:對于每個塊,查詢是遞增的順序排序,所以右指針curR按照遞增的順序移動。在下一個塊的開始時,指針是盡量靠右的 ,將移動到下一個塊中的最小的R處。這意味著對于一個給定的塊,右指針移動的量是
接下來給幾個例題:
小B有一個序列,包含N個1~K之間的整數(shù)。他一共有M個詢問,每個詢問給定一個區(qū)間[L..R],求Sigma(c(i)^2)的值,其中i的值從1到K,其中c(i)表示數(shù)字i在[L..R]中的重復(fù)次數(shù)。小B請你幫助他回答詢問。
第一行,三個整數(shù)N、M、K。 第二行,N個整數(shù),表示小B的序列。 接下來的M行,每行兩個整數(shù)L、R。
M行,每行一個整數(shù),其中第i行的整數(shù)表示第i個詢問的答案。
6 4 3 1 3 2 1 1 3 1 4 2 6 3 5 5 6
6 9 5 2
對于全部的數(shù)據(jù),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 ;}HH有一串由各種漂亮的貝殼組成的項鏈。HH相信不同的貝殼會帶來好運,所以每次散步 完后,他都會隨意取出一段貝殼,思考它們所表達的含義。HH不斷地收集新的貝殼,因此, 他的項鏈變得越來越長。有一天,他突然提出了一個問題:某一段貝殼中,包含了多少種不同 的貝殼?這個問題很難回答。。。因為項鏈實在是太長了。于是,他只好求助睿智的你,來解 決這個問題。
第一行:一個整數(shù)N,表示項鏈的長度。 第二行:N個整數(shù),表示依次表示項鏈中貝殼的編號(編號為0到1000000之間的整數(shù))。 第三行:一個整數(shù)M,表示HH詢問的個數(shù)。 接下來M行:每行兩個整數(shù),L和R(1 ≤ L ≤ R ≤ N),表示詢問的區(qū)間。
M行,每行一個整數(shù),依次表示詢問對應(yīng)的答案。
6 1 2 3 4 3 5 3 1 2 3 5 2 6
2 2 4
對于20%的數(shù)據(jù),N ≤ 100,M ≤ 1000; 對于40%的數(shù)據(jù),N ≤ 3000,M ≤ 200000; 對于100%的數(shù)據(jù),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 ;}HH有一串由各種漂亮的貝殼組成的項鏈。HH相信不同的貝殼會帶來好運,所以每次散步 完后,他都會隨意取出一段貝殼,思考它們所表達的含義。HH不斷地收集新的貝殼,因此, 他的項鏈變得越來越長。有一天,他突然提出了一個問題:某一段貝殼中,包含了多少種不同 的貝殼?這個問題很難回答。。。因為項鏈實在是太長了。于是,他只好求助睿智的你,來解 決這個問題。
第一行:一個整數(shù)N,表示項鏈的長度。 第二行:N個整數(shù),表示依次表示項鏈中貝殼的編號(編號為0到1000000之間的整數(shù))。 第三行:一個整數(shù)M,表示HH詢問的個數(shù)。 接下來M行:每行兩個整數(shù),L和R(1 ≤ L ≤ R ≤ N),表示詢問的區(qū)間。
M行,每行一個整數(shù),依次表示詢問對應(yīng)的答案。
6 1 2 3 4 3 5 3 1 2 3 5 2 6
2 2 4
對于20%的數(shù)據(jù),N ≤ 100,M ≤ 1000; 對于40%的數(shù)據(jù),N ≤ 3000,M ≤ 200000; 對于100%的數(shù)據(jù),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)表示組合數(shù),組合數(shù)C(a, b)等價于在a個不同的物品中選取b個的選取方案數(shù)。
這道題倒是有些需要想想。 但是有一個神奇的事情:被續(xù)了一秒)
其實莫隊還可以套上樹狀數(shù)組或者在一棵樹上搞的,但是這篇文章就簡單先介紹下啦~ 要是俺有空就出莫隊的續(xù)集,嘿嘿~
新聞熱點
疑難解答