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

来源:互联网 发布:激光聚焦矩阵 mc百科 编辑:程序博客网 时间:2024/06/01 18:38

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


1 0