线段树维护区间颜色数量 色板游戏(洛谷P1558)

P1558 色板游戏

题目背景

阿宝上学了,今天老师拿来了一块很长的涂色板。

题目描述

色板长度为L,L是一个正整数,所以我们可以均匀地将它划分成L块1厘米长的小方格。并从左到右标记为1, 2, … L。

现在色板上只有一个颜色,老师告诉阿宝在色板上只能做两件事:

  1. “C A B C” 指在A到 B 号方格中涂上颜色 C。
  2. “P A B” 指老师的提问:A到 B号方格中有几种颜色。

学校的颜料盒中一共有 T 种颜料。为简便起见,我们把他们标记为 1, 2, … T. 开始时色板上原有的颜色就为1号色。 面对如此复杂的问题,阿宝向你求助,你能帮助他吗?

输入格式
第一行有3个整数 L (1 <= L <= 100000), T (1 <= T <= 30) 和 O (1 <= O <= 100000)。 在这里O表示事件数。
接下来 O 行, 每行以 “C A B C” 或 “P A B” 得形式表示所要做的事情(这里 A, B, C 为整数, 可能A> B,这样的话需要你交换A和B)

输出格式
对于老师的提问,做出相应的回答。每行一个整数。

输入 #1

2 2 4
C 1 1 2
P 1 2
C 2 2 2
P 1 2

输出 #1

2
1

显然的线段树,但这是区间染色的线段树,要结合位运算符<或 | >来解决,真的佩服想到这种方法的人;维护每个区间的或位运算值,算出来每个二进制位如果为1那么就代表一个颜色;具体可以模拟一下,1001|0001=1001,代表这两个区间合并只有两种颜色;1001|0110=1111,代表这两个区间合并有4种颜色;要注意每次修改把颜色传化为二进制传进去,每个颜色都有自己二进制表示;最后统计sum有几个1就完事了;
代码:

#include<bits/stdc++.h>
using namespace std;
int x,y,z;
int sum;
struct Node{
	int l,r,f,color;
}tree[400100];
inline void build(int k,int ll,int rr){
	tree[k].l=ll,tree[k].r=rr,tree[k].f=0;
	if(ll==rr){
		tree[k].color=1;
		return;
	}
	int m=(ll+rr)>>1;
	build(k<<1,ll,m);
	build(k<<1|1,m+1,rr);
	tree[k].color=tree[k<<1].color|tree[k<<1|1].color;
}
inline void pd(int k){
	if(tree[k].f){
		tree[k<<1].f=tree[k].f;
		tree[k<<1|1].f=tree[k].f;
		tree[k<<1].color=tree[k].color;
		tree[k<<1|1].color=tree[k].color;
		tree[k].f=0;
		return;
	}
}
inline void change(int k){
	if(tree[k].l>=x&&tree[k].r<=y){
		tree[k].color=z;
		tree[k].f=1;
		return;
	}
	pd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) change(k<<1);
	if(y>m) change(k<<1|1);
	tree[k].color=tree[k<<1].color|tree[k<<1|1].color;
}
inline void ask(int k){
	if(tree[k].l>=x&&tree[k].r<=y){
		sum|=tree[k].color;
		return;
	}
	pd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) ask(k<<1);
	if(y>m) ask(k<<1|1);
}
inline int solve(int k){
	int ans=0;
	while(k){
		if(k&1){
			ans++;
		}
		k>>=1;
	}
	return ans;
}
int main(){
	int L,T,O;
	scanf("%d%d%d",&L,&T,&O);
	build(1,1,L);
	char s[5];
	for(int i=1;i<=O;i++){
		scanf("%s",s);
		if(s[0]=='C'){
			scanf("%d%d%d",&x,&y,&z);
			z=1<<(z-1);//转化为二进制 
			if(x>y){
				int t;
				t=x,x=y,y=t;
			}
			change(1);
		}
		else{
			scanf("%d%d",&x,&y);
			if(x>y){
				int t;
				t=x,x=y,y=t;
			}
			sum=0;
			ask(1);
			printf("%d\n",solve(sum));
		}
	}
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值