P1558 色板游戏

这是一道同样有意思的题……

这道题运用的思路是二进制存储,故和其他线段树有一点不同,算是线段树里面的另一种套路题。

题目大意

有一个长度为\(L\)的板子,现在要在上面涂色。

如果输入\(C\),则在\([L, R]\)这个区间内图上颜色\(C\)(至于说为什么这两个变量名字相同咱也不知道也不敢问……具体情况看题目就可以了,链接在最顶上)

如果输入\(P\),则询问在\([L, R]\)这个区间内可以看到多少种颜色。

解题思路

这道题也算是一个套路题,因为有不同的颜色,所以不能用一般的方法进行存储。

看到这个题目数据显示颜色最多有30种(当然不是因为看到标签里面有二进制),所以想到用二进制来解决。

为什么用二进制?

这道题目最巧的是最多有\(30\)种不同的颜色,刚好用二进制表示int的最大值是\(31\)\(1\)这不是巧合

因此,我们只需要判断这一段区间里面的答案有多少个1就代表有多少种颜色。非常简单吧(不)

需要的数组及变量

sum[]数组:用来创建线段树。

lazy[]数组:用来保存懒标记(传递覆盖颜色的信息,提高算法运行速度)。

nmkxyzopint型变量:题目所给(nmk就是LTOxyz就是ABCop就是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;
}

给个赞吧!

posted @ 2023-02-06 20:19  煎饼Li  阅读(25)  评论(0编辑  收藏  举报