彩色盒子
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 }