题目大意:有一段长为L(1 <= L <= 100000)的木板,T(1 <= T <= 30)种颜料,O(1 <= O <= 100000)个操作:
C A B C ——将区间[A, B]涂为颜色C
P A B —— 查询区间[A, B]间有多少种颜色
PS:初始时木板颜色为1;A不一定小于B。做题时需注意这两个地方。
解题思路:
首先,这是个更新区间,查询区间的问题,很容易想到要用线段树。那么接下来的工作就是线段树中需要哪些数据域来解决这个问题呢?一共有30种颜料,不多,但也不能每个节点开个数组吧,如此,一来太破费空间,二来更新起来麻烦。我们细细想来,一,颜色种类不多,区区30种;二,在木板上,每一个颜色,只有两个状态:1有2无。既然只有两个状态的话,不妨用位操作,刚好颜色种类不多,一个int就够了。那么数据结构解决了,接下来,怎么更新呢?int的每一位代表一种颜色,1表示有,0表示无,那左区间和右区间进行按拉或操作不就能更新整段区间的颜色了吗?所以,最后数二进制中1的个数,就是有多少种颜色了。
具体代码实现:
#include <stdio.h>
#define maxn 100002<<2
#define m ((l+r)>>1)
#define ll rt<<1 // 左孩子
#define rr rt<<1|1 //右孩子
#define lson l,m,ll // 左孩子的参数
#define rson m+1,r,rr //右孩子的参数
int cl[maxn]; // 记录有哪些颜色
int st[maxn]; // 延迟标记
inline int count(int t) { // 用来计算二进制中有多少个1
int c = 0;
while (t) {
c++;
t &= (t - 1); // 每次去掉最后一个1,也可写成 t -= t & -t(取自树状数组)
}
return c;
}
inline void down(int rt) { // 下推延迟标记
if (st[rt]) {
cl[ll] = cl[rr] = st[ll] = st[rr] = st[rt];
st[rt] = 0;
}
}
void update(int L, int R, int c, int l, int r, int rt) {
if (L <= l && r <= R) {
cl[rt] = st[rt] = c;
return;
}
down(rt);
if (L <= m)
update(L, R, c, lson);
if (R > m)
update(L, R, c, rson);
cl[rt] = cl[ll] | cl[rr]; // push up
}
int query(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R)
return cl[rt];
down(rt);
int res = 0;
if (L <= m)
res = query(L, R, lson);
if (R > m)
res |= query(L, R, rson);
return res;
}
int main() {
int n, t, p, a, b, c;
char op;
scanf("%d %d %d", &n, &t, &p);
// 建树
cl[1] = st[1] = 1;
while (p--) {
scanf(" %c %d %d", &op, &a, &b);
if (a > b) { // 别忘了交换一下
a ^= b ^= a ^= b;
}
if (op == 'C') {
scanf("%d", &c);
update(a, b, 1 << (c - 1), 1, n, 1);
} else {
printf("%d\n", count(query(a, b, 1, n, 1)));
}
}
return 0;
}