带修改的莫队

彩色盒子

sol:彩色盒子可以用分块来做(有题解),也可以用莫队来做,带修改操作。
如颜色:1 3 2 5 7 8 2 4 6
Q:[1,9]  //询问区间1~9的颜色数
M:3 4   //将第三个位置的颜色改为颜色4
M:1 5
Q:[1,3]
若用莫队来做,按查询排序,依次得到区间[1,3],[1,9]。
但,在做查询[1,3]时,查询操作的前面有两个修改操作。
怎么解决:定义一个时间now,相当于给每一个操作依次打上一个时间戳,now初值为0,如上面四个操作,now值依次为1,2,3,4.
现在我们做查询[1,3],now=4,那我们就要看在这个询问前是否有修改操作,本样例有两次修改,修改后,再做莫队。
接下来我们做查询[1,9],now=1,我们查询这个区间时本没有两次修改操作,所以又把前面做的两次修改操作撤销。
这样就顺利得到我们想要的结果。

代码如下:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 using namespace std;
  5 typedef long long ll;
  6 #define space putchar(' ')
  7 #define enter putchar('
')
  8 template <class T>
  9 void read(T &x){
 10     char c;
 11     bool op = 0;
 12     while(c = getchar(), c < '0' || c > '9')
 13         if(c == '-') op = 1;
 14     x = c - '0';
 15     while(c = getchar(), c >= '0' && c <= '9')
 16         x = x * 10 + c - '0';
 17     if(op) x = -x;
 18 }
 19 template <class T>
 20 void write(T x){
 21     if(x < 0) x = -x, putchar('-');
 22     if(x >= 10) write(x / 10);
 23     putchar('0' + x % 10);
 24 }
 25 const int N = 10005, M = 1000005, B = 464;
 26 int n, m, pl = 1, pr = 0, cur, res, ans[N], a[N], cnt[M];
 27 int idxC, idxQ, tim[N], pos[N], val[N], pre[N];
 28 #define bel(x) (((x) - 1) / B + 1)
 29 struct query
 30 {
 31     int id, tim, l, r;
 32     bool operator < (const query &b) const
 33     {
 34         if(bel(l) != bel(b.l)) return l < b.l;
 35         if(bel(r) != bel(b.r)) return r < b.r;
 36         return id < b.id;
 37     }
 38 } q[N];
 39 void change_add(int cur)
 40 {
 41     if(pos[cur] >= pl && pos[cur] <= pr) //如果这个修改操作发生在我们当前询问的区间中 
 42     {
 43         cnt[a[pos[cur]]]--; //则从前这个点的color的出现次数-1 
 44         if(!cnt[a[pos[cur]]]) //如果减少后变成了0,则出现的总color数-1 
 45            res--;
 46     }
 47     pre[cur] = a[pos[cur]];//记下从前的color,方便今后如果后面再加回来时进行操作 
 48     a[pos[cur]] = val[cur]; //换上新的color
 49     if(pos[cur] >= pl && pos[cur] <= pr) //同上 
 50     {
 51         if(!cnt[a[pos[cur]]]) //如果新color出现的次数从0变成1,则总color+1 
 52             res++;
 53         cnt[a[pos[cur]]]++;
 54     }
 55 }
 56 void change_del(int cur)
 57 {
 58     if(pos[cur] >= pl && pos[cur] <= pr)  
 59     {
 60         cnt[a[pos[cur]]]--;
 61         if(!cnt[a[pos[cur]]]) res--;
 62     }
 63     a[pos[cur]] = pre[cur];
 64     if(pos[cur] >= pl && pos[cur] <= pr)
 65     {
 66         if(!cnt[a[pos[cur]]]) res++;
 67         cnt[a[pos[cur]]]++;
 68     }
 69 }
 70 void change(int now)
 71 {
 72     while(cur < idxC && tim[cur + 1] <= now)//找now之前未作的修改
 73     //cur代表目前的修改操作进行到哪一个了
74 //cur+1到当前询问时间点now之间还有些修改操作还没有做,加进来  75 change_add(++cur); 76 while(cur && tim[cur] > now) 77 //也有可能是多做了修改操作,于是删除这些修改操作 78 change_del(cur--); 79 } 80 void add(int p) 81 { 82 if(!cnt[a[p]]) 83 res++; 84 cnt[a[p]]++; 85 } 86 void del(int p) 87 { 88 cnt[a[p]]--; 89 if(!cnt[a[p]]) 90 res--; 91 } 92 bool isQ() 93 { 94 char op[2]; 95 scanf("%s", op); 96 return op[0] == 'Q'; 97 } 98 int main() 99 { 100 read(n), read(m); 101 for(int i = 1; i <= n; i++) 102 read(a[i]); 103 for(int i = 1; i <= m; i++) 104 { 105 if(isQ()) //查询操作 106 { 107 idxQ++; 108 q[idxQ].id = idxQ; //表示其为第多少个查询操作 109 q[idxQ].tim = i; //这个查询操作发生的时间 110 read(q[idxQ].l); 111 read(q[idxQ].r); 112 } 113 else //修改操作 114 { 115 tim[++idxC] = i; //第idxc个修改操作发生的时间 116 read(pos[idxC]); //修改的位置 117 read(val[idxC]); //修改后的color 118 } 119 } 120 sort(q + 1, q + idxQ + 1); 121 for(int i = 1; i <= idxQ; i++) 122 { 123 change(q[i].tim); 124 //以当前这个查询为基准,是否有修改操作没有做,或者多做了 125 while(pl > q[i].l) add(--pl); 126 while(pr < q[i].r) add(++pr); 127 while(pl < q[i].l) del(pl++); 128 while(pr > q[i].r) del(pr--); 129 ans[q[i].id] = res; 130 } 131 for(int i = 1; i <= idxQ; i++) 132 write(ans[i]), enter; 133 return 0; 134 }
原文地址:https://www.cnblogs.com/cutepota/p/12887756.html