POJ2777

题目描述:

色板长度为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
#include
#include
#include
#include
#include
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=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;
}


你可能感兴趣的:(codeforces)