先来谈谈lazy思想。做了这么多的线段树,应该总结一下,lazy是一个很经典的思
想。所谓lazy,就是懒惰,每次不想做太多,只要插入的区间完全覆盖了当前结点所管
理的区间就不再往下做了,在当前结点上打上一个lazy标记,然后直接返回。下次如果
遇到当前结点有lazy标记的话,直接传递给两个儿子,自己的标记清空。这样做肯定是
正确的。我们以染色为例,可以这样想,如果当前结点和它的子孙都有lazy标记的话,
必定是子孙的先标记,因为如果是自己先标记,那么在访问子孙的时候,必定会将自己
的标记下传给儿子,而自己的标记必定会清空,那么lazy标记也就不存在了。所以可以
肯定,当前的lazy标记必定覆盖了子孙的,所以直接下传即可,不需要做任何判断。当
然,这是染色问题,是直接赋值的,如果像pku 3468那样,每次是区间加和,则传递标
记的时候不能简单的赋值,必须累加,这是显而易见的。
一看到数据量就可以首先确定是线段树了,经典的区间染色问题,涉及到区间的
更新和询问,和pku 3468 类似,巧妙运用lazy思想。就是每次更新区间段的时候延迟
更新,只是在完全覆盖的区间打上一个lazy标记。这题的询问是求区间段中不同颜色的
数量,因为颜色数不多只有30种,可以巧妙运用二进制位运算,用一个int就可以表示
当前区间段的颜色情况。比如1001表示有两种颜色,如果左子树的当前颜色情况是101
,而右子树的颜色情况是011,那么父亲的颜色情况就是两者的位或,这样就可以避免
掉重复的情况。
#include < stdio.h >
#define N 100010
struct node{
int l,r,c;
}p[N * 3 ];
int flag,count;
void bulid( int k, int s, int t)
{
int kl,kr,mid;
p[k].r = t;p[k].l = s;
p[k].c = 1 ;
if (t == s) return ;
mid = (s + t) >> 1 ;kl = k << 1 ;kr = kl + 1 ;
bulid(kl,s,mid);
bulid(kr,mid + 1 ,t);
}
void updata( int k, int s, int t, int v)
{
int kr,kl,mid;
if (p[k].c == v)
return ;
if (s <= p[k].l && t >= p[k].r)
p[k].c = v;
else {
mid = (p[k].r + p[k].l) >> 1 ;kl = k << 1 ;kr = kl + 1 ;
if (p[k].c !=- 1 )
{
p[kr].c = p[kl].c = p[k].c;
p[k].c =- 1 ;
}
if (s <= mid)updata(kl,s,t,v);
if (t > mid)updata(kr,s,t,v);
}
}
void query( int k, int s, int t)
{
int kr,kl,mid;
if (p[k].c > 0 )
{
int t = p[k].c;
if ((flag & ( 1 << t)) == 0 ) // 由于颜色只有30种,所以可以用位运算来标记状态
{ //注意这里的移位操作,flag的32个位上的1和0分别表示该位的位置代表的
#define N 100010
struct node{
int l,r,c;
}p[N * 3 ];
int flag,count;
void bulid( int k, int s, int t)
{
int kl,kr,mid;
p[k].r = t;p[k].l = s;
p[k].c = 1 ;
if (t == s) return ;
mid = (s + t) >> 1 ;kl = k << 1 ;kr = kl + 1 ;
bulid(kl,s,mid);
bulid(kr,mid + 1 ,t);
}
void updata( int k, int s, int t, int v)
{
int kr,kl,mid;
if (p[k].c == v)
return ;
if (s <= p[k].l && t >= p[k].r)
p[k].c = v;
else {
mid = (p[k].r + p[k].l) >> 1 ;kl = k << 1 ;kr = kl + 1 ;
if (p[k].c !=- 1 )
{
p[kr].c = p[kl].c = p[k].c;
p[k].c =- 1 ;
}
if (s <= mid)updata(kl,s,t,v);
if (t > mid)updata(kr,s,t,v);
}
}
void query( int k, int s, int t)
{
int kr,kl,mid;
if (p[k].c > 0 )
{
int t = p[k].c;
if ((flag & ( 1 << t)) == 0 ) // 由于颜色只有30种,所以可以用位运算来标记状态
{ //注意这里的移位操作,flag的32个位上的1和0分别表示该位的位置代表的
// 颜色是否存在过,先将1左移t个位,则只有第t+1个位上的数字为1,其余为0,
count ++ ; // 当与flag进行位与运算等于0时,则表示所对应的颜色没出现过
flag |= 1 << t; //同样的,若出现过,则用flag在该位上标记一下
}
return ;
}
mid = (p[k].r + p[k].l) >> 1 ;kl = k << 1 ;kr = kl + 1 ;
if (s <= mid) query(kl,s,t);
if (t > mid) query(kr,s,t);
}
int main()
{
int n,m,a,b,x,t;
char ch[ 2 ];
while (scanf( " %d %d %d " , & n, & t, & m) != EOF)
{
bulid( 1 , 1 ,n);
while (m -- )
{
scanf( " %s " ,ch);
if (ch[ 0 ] == 'C')
{
scanf( " %d %d %d " , & a, & b, & x);
if (a > b) { t = a;a = b;b = t;}
updata( 1 ,a,b,x);
}
else {
flag = count = 0 ;
scanf( " %d %d " , & a, & b);
if (a > b){t = a;a = b;b = t;}
query( 1 ,a,b);
printf( " %d\n " ,count);
}
}
}
return 0 ;
}
count ++ ; // 当与flag进行位与运算等于0时,则表示所对应的颜色没出现过
flag |= 1 << t; //同样的,若出现过,则用flag在该位上标记一下
}
return ;
}
mid = (p[k].r + p[k].l) >> 1 ;kl = k << 1 ;kr = kl + 1 ;
if (s <= mid) query(kl,s,t);
if (t > mid) query(kr,s,t);
}
int main()
{
int n,m,a,b,x,t;
char ch[ 2 ];
while (scanf( " %d %d %d " , & n, & t, & m) != EOF)
{
bulid( 1 , 1 ,n);
while (m -- )
{
scanf( " %s " ,ch);
if (ch[ 0 ] == 'C')
{
scanf( " %d %d %d " , & a, & b, & x);
if (a > b) { t = a;a = b;b = t;}
updata( 1 ,a,b,x);
}
else {
flag = count = 0 ;
scanf( " %d %d " , & a, & b);
if (a > b){t = a;a = b;b = t;}
query( 1 ,a,b);
printf( " %d\n " ,count);
}
}
}
return 0 ;
}