【国家集训队】数颜色——带修改莫队

带修改的莫队,还没做过的莫队建议先做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\n",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\n", 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 }
View Code

 


 

posted @ 2018-04-09 21:01  ww3113306  阅读(229)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。