题目描述:
色板长度为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号色。 面对如此复杂的问题,阿宝向你求助,你能帮助他吗?
做法:
这是一道线段树+状压的好题。很显然的T特别的小,也就是说颜色种类很少。那么我们就可以对颜色进行一个状态压缩。
第i种颜色表示2^(i-1)这个数,然后一个区间所对应的数转换成2进制后第i位为1那么就代表它涂有第i这种颜色。然后放到线段树上搞一下就好了
其中我们可以用位操作 | 来合并颜色种类
代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
int d[40005],b[400005],c[400005],e[400005];
int n,m,x,y,T,z;
char ch;
void build(int k,int x,int y)
{
b[k]=x; c[k]=y;
if (x==y){
d[k]=1;
e[k]=1;
return;
}
int mid=(x+y)/2;
build(k*2,x,mid);
build(k*2+1,mid+1,y);
d[k]=1; e[k]=0;
}
void maintain(int k)
{
d[k*2]=d[k];//涂上父亲的颜色
d[k*2+1]=d[k];
e[k*2]=e[k];
e[k*2+1]=e[k];
e[k]=0;
}
void add(int k,int x,int y,int z)
{
if (x>c[k]||y<b[k]) return;
if (x<=b[k]&&y>=c[k]){
d[k]=(1<<(z-1));//覆盖
e[k]=1;//儿子要更新
return;
}
if (e[k]) maintain(k);
int mid=(b[k]+c[k])/2;
if (y<=mid) add(k*2,x,y,z);
else if (x>mid) add(k*2+1,x,y,z);
else if (x<=mid&&y>mid){
add(k*2,x,mid,z);
add(k*2+1,mid+1,y,z);
}
d[k]=d[k*2]|d[k*2+1];
}
int sum(int k,int x,int y)
{
if (x<=b[k]&&y>=c[k]) return d[k];
if (e[k]) maintain(k);
int mid=(b[k]+c[k])/2;
if (y<=mid) return sum(k*2,x,y);
if (x>mid) return sum(k*2+1,x,y);
if (x<=mid&&y>mid) return sum(k*2,x,mid)|sum(k*2+1,mid+1,y);
}
int main()
{
scanf("%d%d%d",&n,&m,&T);
build(1,1,n);
while (T--){
cin>>ch;
if (ch=='C'){
scanf("%d%d%d",&x,&y,&z);
if (x>y) swap(x,y);
add(1,x,y,z);
}
else {
scanf("%d%d",&x,&y);
if (x>y) swap(x,y);
int sum1=sum(1,x,y);
int ans=0;
while (sum1){
if (sum1%2) ans++;
sum1=sum1/2;
}
printf("%d\n",ans);
}
}
return 0;
}