pku2777 线段树(染色问题)

  先来谈谈lazy思想。做了这么多的线段树,应该总结一下,lazy是一个很经典的思
InBlock.gif想。所谓lazy,就是懒惰,每次不想做太多,只要插入的区间完全覆盖了当前结点所管
InBlock.gif理的区间就不再往下做了,在当前结点上打上一个lazy标记,然后直接返回。下次如果
InBlock.gif遇到当前结点有lazy标记的话,直接传递给两个儿子,自己的标记清空。这样做肯定是
InBlock.gif正确的。我们以染色为例,可以这样想,如果当前结点和它的子孙都有lazy标记的话,
InBlock.gif必定是子孙的先标记,因为如果是自己先标记,那么在访问子孙的时候,必定会将自己
InBlock.gif的标记下传给儿子,而自己的标记必定会清空,那么lazy标记也就不存在了。所以可以
InBlock.gif肯定,当前的lazy标记必定覆盖了子孙的,所以直接下传即可,不需要做任何判断。当
InBlock.gif然,这是染色问题,是直接赋值的,如果像pku 3468那样,每次是区间加和,则传递标
InBlock.gif记的时候不能简单的赋值,必须累加,这是显而易见的。

 一看到数据量就可以首先确定是线段树了,经典的区间染色问题,涉及到区间的
InBlock.gif更新和询问,和pku 3468 类似,巧妙运用lazy思想。就是每次更新区间段的时候延迟
InBlock.gif更新,只是在完全覆盖的区间打上一个lazy标记。这题的询问是求区间段中不同颜色的
InBlock.gif数量,因为颜色数不多只有30种,可以巧妙运用二进制位运算,用一个int就可以表示
InBlock.gif当前区间段的颜色情况。比如1001表示有两种颜色,如果左子树的当前颜色情况是101
InBlock.gif,而右子树的颜色情况是011,那么父亲的颜色情况就是两者的位或,这样就可以避免
InBlock.gif掉重复的情况。

#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分别表示该位的位置代表的
// 颜色是否存在过,先将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 ;
}

转载于:https://www.cnblogs.com/nanke/archive/2011/04/28/2032206.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值