题解 P1558 【色板游戏】

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

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

  1. “C A B C” 指在 A A A B B B 号方格中涂上颜色 C

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

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

这道题目,我们发现 1 ≤ t ≤ 30 1\leq t\leq 30 1t30 t t t 的值非常小。

考虑建 t t t 棵线段树,每颗线段树维护是否有这种颜色,需要区间全部清零和区间全部赋值。

我使用的是封装数据结构,注释写的很清楚了

#include <bits/stdc++.h>
#define ls num<<1
#define rs num<<1|1
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
	T RR=1;FF=0;char CH=getchar();
	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
	FF*=RR;
}
const int MAXN=1e5+10;
int a[MAXN];
struct Line_Tree{
	struct Tree{
		int l,r,lazy;//lazy:1为区间赋值,2为区间清零
		bool f;
	}t[MAXN<<2];
	void pushup(int num){
		t[num].f=(t[ls].f||t[rs].f);//只要有一个部分有这种颜色,就说明这个区间有这种颜色。
	}
	void pushdown(int num){
		if(t[num].lazy){
			t[ls].lazy=t[rs].lazy=t[num].lazy;//lazy的传递,由于是区间覆盖,所以直接赋值
			t[ls].f=t[rs].f=(t[num].lazy==1);//1为区间赋值,2为区间清零
			t[num].lazy=0;
		}
	}
	void build(int num,int l,int r,int x){
		t[num].l=l;t[num].r=r;t[num].lazy=false;
		if(l==r){
			t[num].f=(a[l]==x);//显然QAQ
			return;//直接return
		}int mid=(l+r)>>1;
		build(ls,l,mid,x);
		build(rs,mid+1,r,x);
		pushup(num);//注意pushup
	}
	void change1(int num,int l,int r){
		if(t[num].l>=l&&t[num].r<=r){
			t[num].lazy=1;//打标记
			t[num].f=true;//直接操作
			return;//直接return
		}pushdown(num);//注意标记下传
		if(t[ls].r>=l)change1(ls,l,r);
		if(t[rs].l<=r)change1(rs,l,r);
		pushup(num);//注意pushup
	}
	void change2(int num,int l,int r){
		if(t[num].l>=l&&t[num].r<=r){
			t[num].lazy=2;//打标记
			t[num].f=false;//直接操作
			return;//直接return
		}pushdown(num);//注意标记下传
		if(t[ls].r>=l)change2(ls,l,r);
		if(t[rs].l<=r)change2(rs,l,r);
		pushup(num);//注意pushup
	}
	bool query(int num,int l,int r){
		if(t[num].l>=l&&t[num].r<=r)return t[num].f;//直接看
		pushdown(num);//注意标记下传
		if(t[ls].r<l)return query(rs,l,r);
		if(t[rs].l>r)return query(ls,l,r);
		return (query(ls,l,r)||query(rs,l,r));//只要有一个是就是了
	}
}T[32];//线段树
int main(){
	int n,t,E;read(n);read(t);read(E);
	for(int i=1;i<=n;i++)a[i]=1;//初始化
	for(int i=1;i<=t;i++)T[i].build(1,1,n,i);//初始化
	while(E--){
		char f=getchar();int a,b,c;
		for(;f!='C'&&f!='P';)f=getchar();
		read(a);read(b);if(a>b)swap(a,b);
		if(f=='C'){
			read(c);
			for(int i=1;i<=t;i++)//枚举颜色
				if(i==c)T[i].change1(1,a,b);//区间赋值
				else T[i].change2(1,a,b);//区间清零
		}else{
			int s=0;//计数器
			for(int i=1;i<=t;i++)//枚举颜色
				if(T[i].query(1,a,b))s++;//如果有这种颜色
			printf("%d\n",s);//输出
		}
	}
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值