带修改的莫队,还没做过的莫队建议先做HH的项链(板子题)
这里多了一个修改,首先我们要相信一个真理:暴力出奇迹
莫队就是相当与对暴力的一个优化,
于是对于每个询问,我们标记它的时间(也就是前面有几个修改)
然后像普通莫队一样做,如果时间不符合,那就暴力修改or暴力撤销
比如说我们用now记录现在的X(统计到的颜色个数)是建立在哪次修改后的基础上的(即时间)
那么我们用数组存下了每个修改,并且用last存下某次修改前它要修改的那个空里面原本存的颜色
这样有点绕口,我们举个栗子:
比如原数列: 1 3 7 4 6
现在有第二次修改3 5(把第3个修改为5)
那么last[2]=7(当前数)
然后修改为: 1 3 5 4 6
这就是我们暴力向后修改的过程
如果是撤销,那就相当于是把第i次修改的位置改为last[i]即可
然后为了处理方便,我们先移动区间,最后修改颜色到目标时间
修改时注意,如果要修改的这个位置在区间内(这也是先处理区间的原因),那么就更新ans,
否则直接修改
至于排序,,,大概是玄学复杂度吧,具体证明可以参见:
https://www.cnblogs.com/Paul-Guderian/p/6933799.html
--------------2018.10.11--------------优化了代码格式
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define Ri register int 4 #define AC 10500 5 #define D printf("line in %d ",i); 6 int n, m, block, tot, cnt, L, R, now, ll, rr, tt; 7 int color[AC], s[AC], ans[AC], have[AC], X, last[AC]; 8 struct node{ 9 int x, w; 10 }T[AC]; 11 struct node{ 12 int l, r, num, t; 13 }ques[AC]; 14 15 inline int read() 16 { 17 int x = 0;char c = getchar(); 18 while(c > '9' || c < '0') c = getchar(); 19 while(c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar(); 20 return x; 21 } 22 23 bool cmp(node a, node b) 24 { 25 if(a.l / block != b.l / block) return a.l < b.l; 26 else if(a.r != b.r) return a.r < b.r; 27 else return a.t < b.t; 28 } 29 30 void pre() 31 { 32 char c; 33 n = read(), m = read(), block = sqrt(n); 34 for(Ri i = 1; i <= n; i ++) s[i] = read(); 35 for(Ri i = 1; i <= m; i ++) 36 { 37 cin >> c; 38 if(c == 'Q') 39 ques[++cnt].l = read(), ques[cnt].r = read(), ques[cnt].t = tot ,ques[cnt].num = cnt; 40 else T[++tot].x = read(), T[tot].w = read(); 41 } 42 sort(ques + 1, ques + cnt + 1, cmp); 43 } 44 45 void change()//修改时间 46 { 47 while(now < tt) 48 { 49 for(Ri j = ++now; j <= tt; j ++) 50 { 51 int go = T[j].x; 52 if(!last[j]) last[j] = s[go];//存下上一个时间时的颜色,方便撤销 53 if(go < L || go > R) s[go] = T[j].w;//如果不在区间内就直接修改 54 else //不然的话在修改的同时还要更新答案 55 { 56 color[s[go]] --; 57 if(!color[s[go]]) X --; 58 s[go] = T[j].w; 59 if(!color[s[go]]) X ++; 60 color[s[go]] ++; 61 } 62 } 63 now = tt; 64 } 65 while(now > tt) 66 { 67 for(Ri j = now; j > tt; j --) 68 { 69 int go = T[j].x; 70 if(go < L || go > R) s[go] = last[j];//改回去 71 else 72 { 73 color[s[go]] --; 74 if(!color[s[go]]) X --; 75 s[go] = last[j]; 76 if(!color[s[go]]) X ++;//如果还没有这个颜色就统计入答案 77 color[s[go]] ++; 78 } 79 } 80 now = tt; 81 } 82 } 83 84 void work() 85 { 86 now = ques[1].t ,L = ques[1].l ,R = ques[1].r; 87 for(Ri i = 1; i <= now; i ++) 88 { 89 int go = T[i].x;//error!!!凡是修改了都要记录last 90 last[i] = s[go], s[go]=T[i].w; 91 } 92 for(Ri i = L; i <= R; i ++) 93 { 94 if(!color[s[i]]) X ++; 95 color[s[i]] ++; 96 } 97 ans[ques[1].num] = X; 98 for(Ri i = 2; i <= cnt; i ++)//莫队 99 { 100 ll = ques[i].l, rr = ques[i].r, tt = ques[i].t; 101 while(L < ll) 102 { 103 color[s[L]] --; 104 if(!color[s[L]]) X --; 105 ++ L; 106 } 107 while(L > ll) 108 { 109 if(!color[s[-- L]]) X ++; 110 color[s[L]] ++; 111 } 112 while(R > rr) 113 { 114 color[s[R]] --; 115 if(!color[s[R]]) X --; 116 -- R; 117 } 118 while(R < rr) 119 { 120 if(!color[s[++ R]]) X ++; 121 color[s[R]] ++; 122 } 123 change();//修改时间放入函数以显简洁 124 ans[ques[i].num] = X; 125 } 126 for(Ri i = 1; i <= cnt; i ++) printf("%d ", ans[i]); 127 } 128 129 int main() 130 { 131 freopen("in.in","r",stdin); 132 pre(); 133 work(); 134 fclose(stdin); 135 return 0; 136 }