题目链接: Count Color
大致题意
有n个位置, 从1~n标号, 有两种操作:
① C l r c 把区间[l, r]染色为c
②P l r 询问[l, r]区间有多少种颜色.
解题思路
线段树区间染色
我拿到这题后, 这题不是傻逼题吗? 然后写了3h
其实以前我是有博客写过关于线段树区间染色问题的, ➡️ 博客链接, 我们只需要用一个懒标记表示当前区间是什么颜色即可, 每次更新的时候, 我们都下放懒标记, 查询的时候不需要下放懒标记.
那么这个题我为什么做了这么久呢? (原因是网上的博客的写法都和我不一样)
重点还是在查询这里, 我做过的那道题是每次修改后对于根节点查询, 而本题是一个**[l, r]的区间查询**, 因此这个题我在查询函数的时候, 自然而然的写成了(l <= t[x].l && r >= t[x].r)
形式, 但是这样是不对的.
无论是对于根节点查询, 还是区间查询, 查询的条件都只是, 这个点是不是有懒标记的, 有懒标记返回即可
如照上述做法, 只在[l, r]区间判断是否存在懒标记, 则你在查询时就必须要进行pushdown, 否则会导致查询结果不对. 而你一直pushdown的话, 若根节点已经有了懒标记, 你下放后到其子节点, 其子节点也必然有懒标记, 以此类推, 单次查询复杂度直接爆炸到nlogn, 还不如暴力了.
AC代码
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
struct node {
int l, r;
int val;
}t[N << 2];
void pushdown(node& op, int lazy) { op.val = lazy; }
void pushdown(int x) {
if (!t[x].val) return;
pushdown(t[x << 1], t[x].val), pushdown(t[x << 1 | 1], t[x].val);
t[x].val = 0;
}
void build(int l, int r, int x = 1) {
t[x] = { l, r, 1 };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
}
void modify(int l, int r, int c, int x = 1) {
if (l <= t[x].l && r >= t[x].r) {
pushdown(t[x], c);
return;
}
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(l, r, c, x << 1);
if (r > mid) modify(l, r, c, x << 1 | 1);
}
int ask(int l, int r, int x = 1) {
if (t[x].val) return t[x].val;
int mid = t[x].l + t[x].r >> 1;
int res = 0;
if (l <= mid) res |= ask(l, r, x << 1);
if (r > mid) res |= ask(l, r, x << 1 | 1);
return res;
}
int fact(int x) {
int res = 0;
while (x) res++, x -= x & -x;
return res;
}
int main()
{
int n, col, m; cin >> n >> col >> m;
build(1, n);
rep(i, m) {
char s[2]; int l, r; scanf("%s %d %d", s, &l, &r);
if (l > r) swap(l, r);
if (*s == 'C') {
int c; scanf("%d", &c);
modify(l, r, 1 << (c - 1));
}
else printf("%d\n", fact(ask(l, r)));
}
return 0;
}
QAQ 希望本文能帮到您.