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; }