题目链接: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;
}