1. 程式人生 > >莫隊演算法——解決序列上詢問的利器 (2) 帶修改的莫隊

莫隊演算法——解決序列上詢問的利器 (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 ; }