POJ 2777 Count Color (线段树区间修改 + 状态压缩)

题目链接:POJ 2777 Count Color


【题目大意】

给你 n 块板子, 编号1--n , 板子的颜色最多30种, 初始时  板子的颜色都是 1;

有两种操作 

              1 。把给定区间的板子染成一种颜色

              2 。查询给定区间有多少种不同的颜色


此题一看便是线段树的区间修改问题 , 然而对于统计有多少种不同颜色 , 开始想用set 来操作, 后来发现用set每次查询都要走到树的叶子节点,并不能运用线段树的高效率查询。 后来学到了 二进制状态压缩的方法 。(又是状态压缩,我怎么没想到捏!)

30种颜色 ,我们用二进制来存 , 每一位对于一种颜色即可。


【源代码】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define L(m) m<<1
#define R(m) m<<1|1
using namespace std;
const int maxn = 100000+5;
int ans =0 ;
struct node{
	int l,r,color;
	bool setv; //懒惰标记
}tree[maxn<<2];
void Build(int m,int l,int r){
	tree[m].l=l ; tree[m].r=r;
	if(tree[m].l==tree[m].r){
		tree[m].color=1;
		tree[m].setv = 0; //不要忘了赋初值
		return ;
	}
	int mid = (l+r)>>1;
	Build(L(m),l,mid);
	Build(R(m),mid+1,r);
	tree[m].color = tree[L(m)].color | tree[R(m)].color; //回溯,也可以写maintain(m);
}
void pushdown(int m){
	if(tree[m].l==tree[m].r) return;
	if(tree[m].setv){
		int tmp = tree[m].color;
		tree[L(m)].color = tree[R(m)].color = tmp;
		tree[L(m)].setv= true;
		tree[R(m)].setv= true;
		tree[m].setv = false;
	}
}
void maintain(int m){
	tree[m].color = tree[L(m)].color | tree[R(m)].color;
}
void Update(int m,int l,int r,int x){
	if(tree[m].l>=l && tree[m].r<=r){
		tree[m].setv = true;
		tree[m].color = (1<<(x-1)); //存入2的几次方
		return ;
	}
	pushdown(m);
	int mid = (tree[m].l+tree[m].r)>>1;
	if(mid>=l)
		Update(L(m),l,r,x);
	if(mid<r)
		Update(R(m),l,r,x);
	maintain(m);
}
void Query(int m,int l,int r){
	if( (tree[m].l >= l && tree[m].r <= r)){
		ans |=tree[m].color; //用 或 操作来实现颜色的累加
		return 	;
	}
	pushdown(m);
	int mid = (tree[m].l + tree[m].r)>>1;
	if(mid>=l)
		Query(L(m),l,r);
	if(mid<r)
		Query(R(m),l,r);
}
int main(){
	int n,m,t;
	scanf("%d%d%d",&n,&m,&t);
		Build(1,1,n);
		char cmd;
		int a,b,c;
		while(t--){
			scanf(" %c",&cmd);
			scanf("%d%d",&a,&b);
			if(a>b)
				swap(a,b);
			if(cmd=='C'){
				scanf("%d",&c);
				Update(1,a,b,c);
			}
			else{
				ans = 0;
				int count = 0;
				Query(1,a,b);
				for(int i=0;i<m;i++){
					if(ans & (1<<i)) //与每一个二进制位进行 与 操作 , 相同说明有这个颜色
						count++;
				}
				printf("%d\n",count);
			}
		}
	
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值