莫隊演算法——解決序列上詢問的利器 (2) 帶修改的莫隊
普通的莫隊戳這裡。
還是考慮類似的問題:有一個長為N序列,有M個操作:1.詢問:在區間[L,R]內,出現了多少個不同的數字。2.修改,將第x個數改為v(序列中所有數字均小於K)。題目會給出K。
做法其實是類似的,只是要考慮更新的問題。
有一種值得思考的做法:首先離線下所有的修改操作。對於某一次詢問[L,R], 先假設沒有任何修改,算出答案來。再考慮所有在這次詢問之前的修改操作,如果修改的數在這個詢問的區間內,則更新答案。
我們仍然按照以前的方法將所有詢問離線後排序,只是要記錄在它之前一共經歷了多少次修改操作。由於沒有必要對於每一次詢問都從第1次修改開始修改陣列,我們可以再加入一個curT(time),代表當前完成了多少次修改,每一次詢問就像curL和curR的移動一樣移動curT就行了。
例題:BZOJ2120 數顏色
Description
墨墨購買了一套N支彩色畫筆(其中有些顏色可能相同),擺成一排,你需要回答墨墨的提問。墨墨會像你釋出如下指令: 1、 Q L R代表詢問你從第L支畫筆到第R支畫筆中共有幾種不同顏色的畫筆。 2、 R P Col 把第P支畫筆替換為顏色Col。為了滿足墨墨的要求,你知道你需要幹什麼了嗎?
Input
第1行兩個整數N,M,分別代表初始畫筆的數量以及墨墨會做的事情的個數。第2行N個整數,分別代表初始畫筆排中第i支畫筆的顏色。第3行到第2+M行,每行分別代表墨墨會做的一件事情,格式見題幹部分。
Output
對於每一個Query的詢問,你需要在對應的行中給出一個數字,代表第L支畫筆到第R支畫筆中共有幾種不同顏色的畫筆。
Sample Input
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
Sample Output
4
4
3
4
HINT
對於100%的資料,N≤10000,M≤10000,修改操作不多於1000次,所有的輸入資料中出現的所有整數均大於等於1且不超過10^6。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std ;
bool Read ( int &x ) { char c = getchar() ; x = 0 ; bool f = 0 ; 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 ; }
void Print ( int x ) { int len = 0, a[50] ; if ( x == 0 ) { putchar('0') ; return ; } if ( x < 0 ) { putchar('-') ; x = -x ; } while (x) { a[++len] = x%10 ; x /= 10 ; } while (len) putchar(a[len--]+'0') ;}
const int maxn = 1e5+10, maxm = 1e6 ;
int n, m, a[maxn], cnt[maxm], answer, num, tot, curL, curR, curT, block ;
struct Query {
int L, R, lst, id, ans ;
friend bool operator < ( Query a, Query b ) {
return (a.L/block==b.L/block) ? a.R < b.R : a.L < b.L ;
}
} q[maxn] ;
bool cmp ( Query a, Query b ) {
return a.id < b.id ;
}
struct Update {
int x, vnew, vold ;
} u[maxn] ;
void add ( int pos ) {
if ( ++cnt[ a[pos] ] == 1 ) ++ answer ;
}
void remove ( int pos ) {
if ( --cnt[ a[pos] ] == 0 ) -- answer ;
}
void add_update ( int wea ) {
u[wea].vold = u[wea].x[a] ;
if ( u[wea].x >= curL && u[wea].x <= curR ) remove(u[wea].x) ;
u[wea].x[a] = u[wea].vnew ;
if ( u[wea].x >= curL && u[wea].x <= curR ) add(u[wea].x) ;
}
void remove_update ( int wea ) {
u[wea].vnew = u[wea].x[a] ;
if ( u[wea].x >= curL && u[wea].x <= curR ) remove(u[wea].x) ;
u[wea].x[a] = u[wea].vold ;
if ( u[wea].x >= curL && u[wea].x <= curR ) add(u[wea].x) ;
}
char cmd[5] ;
int main() {
Read(n) ; Read(m) ;
block = floor( sqrt(n)+0.5 ) ;
curL = 1 ;
curR = 0 ;
curT = 0 ;
int i, j, k, l, r ;
for ( i = 1 ; i <= n ; i ++ )
Read(a[i]) ;
while (m--) {
scanf ( "%s", cmd ) ;
if ( cmd[0] == 'Q' ) {
Read(l) ; Read(r) ;
q[++tot] = (Query) { l, r, num, tot } ;
} else {
Read(l) ; Read(k) ;
u[++num] = (Update){ l, k, 0 } ;
}
}
sort ( q+1, q+tot+1 ) ;
for ( i = 1 ; i <= tot ; i ++ ) {
while ( curL < q[i].L )
remove(curL++) ;
while ( curL > q[i].L )
add(--curL) ;
while ( curR < q[i].R )
add(++curR) ;
while ( curR > q[i].R )
remove(curR--) ;
while ( curT < q[i].lst )
add_update(++curT) ;
while ( curT > q[i].lst )
remove_update(curT--) ;
q[i].ans = answer ;
}
sort ( q+1, q+tot+1, cmp ) ;
for ( i = 1 ; i <= tot ; i ++, putchar('\n') )
Print(q[i].ans) ;
return 0 ;
}