You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
3.6 KiB

2 years ago
##[$POJ$ $2777$ $Count$ $Color$](http://poj.org/problem?id=2777)
### 一、题目大意
题意:有$L$块连续的板子,每块板子最多染一种颜色,有$T$种($<=30$)颜色,刚开始将所有板子染成颜色$1$$O$次操作(包括将$[a,b]$染成颜色$k$,和询问$[a,b]$的不同颜色数),输出每次询问的值。
### 二、题目分析
经典的区间染色问题。
因为总共的颜色最多只有$30$种,因此我们可以用一个范围在$int$的二进制数$bit$存储每一种颜色($bit$的二进制下的每一位代表着一种颜色)
之后我们只需要用线段树对区间上的$bit$进行维护即可。注意在我们区间合并$pushup$的过程中,我们需要将某个结点的左右儿子的值都或起来,作为该结点的值。
之后对于操作$C$,我们只需要将区间$[l,r]$的值更新即可。
对于操作$Q$,我们只需要将区间$[l,r]$的$bit$求出,并求出$bit$在二进制位下的$1$的个数为答案。
`ps`:这个问题种还有一个坑点,对于每一个操作种的$l$和$r$,题目中并没有说明哪个是左区间,哪个是有区间,因此我们还需要特判一下大小。
### 三、实现代码
```cpp {.line-numbers}
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 100010;
#define ls u << 1
#define rs u << 1 | 1
struct Node {
int l, r;
int add, sum;
} tr[N << 2];
void pushup(int u) {
tr[u].sum = tr[ls].sum | tr[rs].sum; //因为是模拟二进制的操作,所以采用 或 运算即可完成拼接操作
}
void pushdown(int u) {
if (tr[u].add) {
tr[ls].add = tr[rs].add = tr[u].add;
tr[ls].sum = tr[rs].sum = tr[u].add;
tr[u].add = 0;
}
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].sum = 1; // sum记录的其实是一个30位的二进制压缩模拟数字此时置为1表示最后一位即0位为1是默认的颜色
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int c) {
if (l <= tr[u].l && tr[u].r <= r) {
tr[u].add = 1 << (c - 1);
tr[u].sum = 1 << (c - 1);
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, c);
if (r > mid) modify(u << 1 | 1, l, r, c);
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
int ans = 0;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) ans = query(u << 1, l, r);
if (r > mid) ans = ans | query(u << 1 | 1, l, r);
return ans;
}
//手工版本数字1个数
int count(int x) {
int cnt = 0;
while (x) {
cnt++;
x -= (x & -x);
}
return cnt;
}
int main() {
//加快读入
ios::sync_with_stdio(false), cin.tie(0);
int n, t, m; //区间[1,n],t种颜色,m个操作
cin >> n >> t >> m;
//构建
build(1, 1, n);
while (m--) {
char op;
int l, r;
cin >> op >> l >> r;
if (l > r) swap(l, r); //坑点题目没说哪个大哪个小,需要特判
if (op == 'C') {
int c;
cin >> c;
modify(1, l, r, c);
} else {
int ans = query(1, l, r);
int cnt = count(ans); //获取某个数二进制位1的个数
printf("%d\n", cnt);
}
}
return 0;
}
```