题意:
n个数,m个操作。
- 查询区间[a,b]内不同数字数
- 将某个数修改为c
题解:
带修改莫队。
与普通莫队不同的就是要记录一下每个查询操作前有多少个修改操作,然后暴力修改或改回去。
题目链接:https://www.luogu.org/problemnew/show/P1903
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define LL long long #define RI register int using namespace std; const int INF = 0x7ffffff ; const int S = 1e6 + 10 ; const int N = 10000 + 10 ; inline int read() { int k = 0 , f = 1 ; char c = getchar() ; for( ; !isdigit(c) ; c = getchar()) if(c == '-') f = -1 ; for( ; isdigit(c) ; c = getchar()) k = k*10 + c-'0' ; return k*f ; } struct Que { int l, r, ti, id ; }q[N] ; int n, m, qcnt = 0, rcnt = 0, hh[N], pp[1010], cc[1010], res[N], pos[N], num[S], ans = 0 ; int L = 1, R = 0, T = 0 ; inline bool cmp1(Que s,Que t) { // 其实我也不知道怎么排序最快qaq if(pos[s.l] == pos[t.l]) { if(pos[s.r] == pos[t.r]) return s.ti < t.ti ; return s.r < t.r ; } return s.l < t.l ; } inline void add(int x) { if(!num[hh[x]]) ans ++ ; num[hh[x]] ++ ; } inline void del(int x) { num[hh[x]] -- ; if(!num[hh[x]]) ans -- ; } inline void addt(int t) { if(pp[t] >= L && pp[t] <= R) { num[hh[pp[t]]] -- ; if(!num[hh[pp[t]]]) ans -- ; if(!num[cc[t]]) ans ++ ; num[cc[t]] ++ ; } swap(hh[pp[t]],cc[t]) ; // 这是一步很神奇的操作 // 因为记录每个位置的历史值显然是十分麻烦的。 // 我们可以观察一个性质就是当一个修改操作被改回去时,就是原值变成修改值,修改值变成原值 // 所以只要交换一下即可 } int main() { n = read(), m = read() ; int base = sqrt(n) ; for(int i=1;i<=n;i++) hh[i] = read(), pos[i] = (i-1)/base + 1 ; char cs ; for(int i=1;i<=m;i++) { cin>>cs ; if(cs == 'Q') { q[++qcnt].l = read(), q[qcnt].r = read() ; q[qcnt].ti = rcnt ; q[qcnt].id = qcnt ; } else { pp[++rcnt] = read(), cc[rcnt] = read() ; } } sort(q+1,q+qcnt+1,cmp1) ; for(int i=1;i<=qcnt;i++) { while(L < q[i].l) del(L++) ; while(L > q[i].l) add(--L) ; while(R < q[i].r) add(++R) ; while(R > q[i].r) del(R--) ; // 以上与普通莫队相同 while(T < q[i].ti) addt(++T) ; while(T > q[i].ti) addt(T--) ; // 为什么都是add操作呢?看看addt函数就知道了qwq res[q[i].id] = ans ; } for(int i=1;i<=qcnt;i++) { printf("%d\n",res[i]) ; } return 0 ; }
PS:scanf读单字符时不忽略空格与换行符T_T,换用cin即可.