P1558 色板游戏
这道题运用的思路是二进制存储,故和其他线段树有一点不同,算是线段树里面的另一种套路题。
题目大意
有一个长度为\(L\)的板子,现在要在上面涂色。
如果输入\(C\),则在\([L, R]\)这个区间内图上颜色\(C\)(至于说为什么这两个变量名字相同咱也不知道也不敢问……具体情况看题目就可以了,链接在最顶上)
如果输入\(P\),则询问在\([L, R]\)这个区间内可以看到多少种颜色。
解题思路
这道题也算是一个套路题,因为有不同的颜色,所以不能用一般的方法进行存储。
看到这个题目数据显示颜色最多有30种(当然不是因为看到标签里面有二进制),所以想到用二进制来解决。
为什么用二进制?
这道题目最巧的是最多有\(30\)种不同的颜色,刚好用二进制表示int
的最大值是\(31\)个\(1\)。这不是巧合
因此,我们只需要判断这一段区间里面的答案有多少个1就代表有多少种颜色。非常简单吧(不)
需要的数组及变量
sum[]
数组:用来创建线段树。
lazy[]
数组:用来保存懒标记(传递覆盖颜色的信息,提高算法运行速度)。
n
,m
,k
,x
,y
,z
,op
之int
型变量:题目所给(n
,m
,k
就是L
,T
,O
;x
,y
,z
就是A
,B
,C
;op
就是C
)。
设计代码
calc()
函数
因为我们知道\(2^{31}\)是int
的极限,同时用二进制表示\(2^{31}\)就是用\(31\)个\(1\)来表示,因此我们用\(1\)位代表\(1\)个颜色。
我们人类都习惯十进制,那么这个就是方便后续计算有多少种颜色(也就是这个二进制数里面有多少个\(1\),也是\(2\)的几次方)。
int calc(int x) {
int temp = 1;
for (int i = 1; i <= x; i++)
temp *= 2;
return temp;
}
push_up()
函数
现在就需要使用位运算之或运算了。这里就是用来计算这一段区间里面最多有集中颜色。
什么是或运算?
答:Google it。
void push_up(int root) {
sum[root] = sum[root << 1] | sum[root << 1 | 1];
}
build()
函数
现在就是快乐建树时间。
我们需要把树上得每一个节点都初始化为\(1\)。
为什么呢?
因为\(2^0 = 1\),也就是说当前有0种颜色(确实啊:刚开始没有颜色,现在明白二进制的用处了吧:就是看答案是2的几次方来判断有几种颜色)。
void build(int l, int r, int root) {
if (l == r) {
sum[root] = 1;
return;
}
int mid = (l + r) >> 1;
build(l, mid, root << 1);
build(mid + 1, r, root << 1 | 1);
push_up(root);
}
push_down()
函数
就算这个题只是计算有多少种颜色,还是用lazy tag
(懒标记)来简单优化一下八。
这个lazy tag
就很简单,可以参照用线段树替换值得代码种的push_down()
函数。
反正题目也是说每一种颜色会覆盖另一种颜色,那干嘛不是替换呢
void push_down(int root) {
if (lazy[root]) {
lazy[lroot] = lazy[root];
lazy[rroot] = lazy[root];
sum[lroot] = lazy[root];
sum[rroot] = lazy[root];
lazy[root] = 0;
}
}
update()
函数
这个函数就是用来修改(替换)当前这一段区间的颜色。
我们首先使用一个临时变量temp
来算出当前的颜色(用二进制表示就是有temp
个1代表一种新的颜色)。
然后,就是常规操作:将sum[root]
替换成temp
,同时告诉lazy[root]
也要替换成temp
。
那这不就和线段树区间替换的update()
函数一毛一样了嘛。只是存颜色的时候特殊一点用了二进制。
void update(int ledge, int redge, int newval, int l, int r, int root) {
if (ledge <= l && r <= redge) {
push_down(root);
int temp = calc(newval - 1);
sum[root] = temp;
lazy[root] = temp;
return;
}
int mid = (l + r) >> 1;
push_down(root);
if (ledge <= mid)
update(ledge, redge, newval, l, mid, root << 1);
if (redge > mid)
update(ledge, redge, newval, mid + 1, r, root << 1 | 1);
push_up(root);
}
query()
函数
非常常规的query()
函数。
和各种模板都非常类似。只是在求ans
的时候会用到或运算(求出当前有多少个颜色,暂时用十进制保存)。
int query(int ledge, int redge, int l, int r, int root) {
if (ledge <= l && r <= redge)
return sum[root];
int mid = (l + r) >> 1;
int ans = 0;
push_down(root);
if (ledge <= mid)
ans = ans | query(ledge, redge, l, mid, root << 1);
if (redge > mid)
ans = ans | query(ledge, redge, mid + 1, r, root << 1 | 1);
return ans;
}
check()
函数
顾名思义,就是来判断当前这个十进制数转化成二进制后有多少个\(1\)(有多少种颜色)。
那就非常简单了。
int check(int x) {
int res = 0;
while (x != 0) {
if (x % 2 == 1)
res++;
x /= 2;
}
return res;
}
main()
主函数
这个主函数就没什么特别的,只是需要注意题目所说:\(x\)可能比\(y\)大,因此在读入过程中如果遇到这种情况就需要swap()
一下。
其他地方就是普通线段树。
cin >> n >> m >> k;
build(1, n, 1);
for (int i = 1; i <= k; i++) {
cin >> op;
if (op == 'C') {
cin >> x >> y >> z;
if (x > y)
swap(x, y);
update(x, y, z, 1, n, 1);
}
else if (op == 'P') {
cin >> x >> y;
if (x > y)
swap(x, y);
int ans = query(x, y, 1, n, 1);
cout << check(ans) << endl;
}
}
完整代码
狗洛谷,居然最后一个Subtask 11
空间要开\(10^6\)才能过(氧化钙明明题目说了\(L\)是\(10^5\))……
#include <bits/stdc++.h>
#define lson l, mid, root << 1
#define rson mid + 1, r, root << 1 | 1
#define lroot root << 1
#define rroot root << 1 | 1
using namespace std;
const int N = 1e5 + 10;
int sum[N << 2], lazy[N << 2];
int calc(int x) {
int temp = 1;
for (int i = 1; i <= x; i++)
temp *= 2;
return temp;
}
void push_up(int root) {
sum[root] = sum[lroot] | sum[rroot];
}
void build(int l, int r, int root) {
if (l == r) {
sum[root] = 1;
return;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
push_up(root);
}
void push_down(int root) {
if (lazy[root]) {
lazy[lroot] = lazy[root];
lazy[rroot] = lazy[root];
sum[lroot] = lazy[root];
sum[rroot] = lazy[root];
lazy[root] = 0;
}
}
void update(int ledge, int redge, int newval, int l, int r, int root) {
if (ledge <= l && r <= redge) {
push_down(root);
int temp = calc(newval - 1);
sum[root] = temp;
lazy[root] = temp;
return;
}
int mid = (l + r) >> 1;
push_down(root);
if (ledge <= mid)
update(ledge, redge, newval, lson);
if (redge > mid)
update(ledge, redge, newval, rson);
push_up(root);
}
int query(int ledge, int redge, int l, int r, int root) {
if (ledge <= l && r <= redge)
return sum[root];
int mid = (l + r) >> 1;
int ans = 0;
push_down(root);
if (ledge <= mid)
ans = ans | query(ledge, redge, lson);
if (redge > mid)
ans = ans | query(ledge, redge, rson);
return ans;
}
int check(int x) {
int res = 0;
while (x != 0) {
if (x % 2 == 1)
res++;
x /= 2;
}
return res;
}
int n, m, k, x, y, z;
char op;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
build(1, n, 1);
for (int i = 1; i <= k; i++) {
cin >> op;
if (op == 'C') {
cin >> x >> y >> z;
if (x > y)
swap(x, y);
update(x, y, z, 1, n, 1);
}
else if (op == 'P') {
cin >> x >> y;
if (x > y)
swap(x, y);
int ans = query(x, y, 1, n, 1);
cout << check(ans) << endl;
}
}
return 0;
}
给个赞吧!