线段树之六

#include <iostream>        //poj 2777 Count Color  线段树+位运算
using namespace std;
struct node
{
int l,r,color; //采用位运算代替用一个vis[]数组来表示哪些颜色存在或不存在,这样既省空间也省时间
}tree[300000];
int c,count;
void build(int s,int t,int num)
{
tree[num].l
=s;tree[num].r=t;
tree[num].color
=1;
if(s!=t)
{
int mid=(s+t)/2;
build(s,mid,num
*2);
build(mid
+1,t,num*2+1);
}
}
int calculate(int p) //计算出p值所含的颜色种类数
{
int s=0;
while(p>0)
{
s
++;
p
-=(p&(-p));
}
return s;
}
void modify(int a,int b,int num)
{

//因为在插入一个区间时,当插入区间等于当前区间时,当前区间的颜色
//数目就变为了1,当前区间的孩子区间也全都变为1,所以不须一直更新到最底层
//等到重新插入另一个区间时再来更新孩子区间的颜色数,如果一直更新到最底层,则会TLE

if(calculate(tree[num].color)==1&&tree[num].l<tree[num].r) //tree[num].l<tree[num].r,如果是叶子节点,则无需更新左右子树
{
tree[num
*2].color=tree[num*2+1].color=tree[num].color; //更新孩子区间的颜色数,如果该区间的颜色数不是1的话,则它的孩子区间也必已更新了
}


if(tree[num].l==a&&tree[num].r==b) //只有当插入区间等于当前区间时,才不用去更新它的孩子区间,每次插入时上面的if语句已经更新了
tree[num].color=c;
else
{
if(b<=tree[num*2].r)
modify(a,b,num
*2);
else if(a>=tree[num*2+1].l)
modify(a,b,num
*2+1);
else
{
int mid=(tree[num].l+tree[num].r)/2; //注意不是 mid=(a+b)/2;
modify(a,mid,num*2);
modify(mid
+1,b,num*2+1);
}
tree[num].color
=tree[num*2].color | tree[num*2+1].color;
}
}
void query(int a,int b,int num)
{
if(calculate(tree[num].color)==1&&tree[num].l<tree[num].r)
{
tree[num
*2].color=tree[num*2+1].color=tree[num].color;
}
if(a==tree[num].l&&b==tree[num].r)
count
=count | tree[num].color;
else
{
if(b<=tree[num*2].r)
query(a,b,num
*2);
else if(a>=tree[num*2+1].l)
query(a,b,num
*2+1);
else
{
int mid=(tree[num].l+tree[num].r)/2; //注意不是 mid=(a+b)/2;
query(a,mid,num*2);
query(mid
+1,b,num*2+1);
}
}
}
int main()
{
int L,T,O,a,b;char ch;
cin
>>L>>T>>O;
build(
1,L,1);
while(O--)
{
cin
>>ch;
if(ch=='C')
{
scanf(
"%d%d%d",&a,&b,&c);
if(a>b)swap(a,b);
c
=1<<c-1; //相当于c=1<<(c-1)
modify(a,b,1);
}
else
{
count
=0;
scanf(
"%d%d",&a,&b);
if(a>b)swap(a,b);
query(a,b,
1);
printf(
"%d\n",calculate(count));
}
}
return 0;
}



//采用位运算可以大大节约时间
//颜色的表示方法。题目上有说明“T<=30”,颜色的种类不多(2<<T在int 范围内),因此使用二进制来存储颜
//色的信息,第n位上有 1 则表示是颜色n。具体说来,就是用1表示颜色1,10表示颜色2,100表示颜色3, 1001
//= 9表示 1 , 4 这2种颜色 ……依次类推,这样表示有一种好处,要统计出 不重复 的颜色的种类,假设某
//线段上原来有颜色 2 ,现在在某部分涂上颜色 2 ,那么对 2 和 2 进行 或 | 运算, 还是 10 , 如果再
//在某部分涂上颜色4 (1000),与原来的 10 进行 或 | 运算,得到 1010 ,在这个二进制数找出有几个1,
//就可以求出这条线段包含有几种不重复的颜色。有两个 1,说明有两种颜色( 2 和 4)。如何在二进制数找出
//有几个 1 , 可以这样统计 1 的个数:
//int calculate(int p)
//{
// int s=0;
// while(p>0)
// {
// s++;
// p-=(p&(-p)); //每次都把右边起的第1个 1 变成 0
// }
// return s;
//}
//比如10100, 10100&(-10100)=100, 10100-100=10000
//10000&(-10000)=10000, 10000-10000=0 至此循环终止, s=2, 表示有两个1

  

posted on 2011-07-17 02:02  sysu_mjc  阅读(162)  评论(0编辑  收藏  举报

导航