普通的莫队戳这里。
还是考虑类似的问题:有一个长为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 ;
}