POJ 2777 段更新段查询 位运算优化

近来A题十分的慢啊,可能是我不在状态吧,每天给自己定的8题计划都完成不了。

依旧线段树,感觉区间合并和计算几何的线段树还是完全没有感觉。现在在深入理解算法的实质,以及在实际应用中怎样转换。线段树只是一种数据结构,只是一棵树具体要怎样使用以及实现怎样的功能,都要Acmer自己来设计,比如说区间合并用的就是lsum,msum,rsum三个数组记录左,当前,又边节点的区间状况。于是乎对这三个数组进行合并与更新操作。线段树中需要构思的就是PushDown和PushUp两个函数了,因为不同情况的线段树中,他们内部的代码差异会很大。另外求和与求最大值,都是比较简单的东西。

今天做的这个题目,开始卡了很久。如果两棵子树中都有相同的颜色呢?那么就要用到集合合并了,对当前点进行集合合并,然后一层层PushUp???这样也太麻烦了,况且用并查集来处理这种情况,也不是那么好处理的,更新太多反而耗时。

题目中的关键点在于颜色种类<=30,这样直接用位运算的或操作就可以了~左右子树或一下,马上就OK了~

其他部分还是和普通的段查询线段树一样。


另外这题的build函数可以不用更新到叶子节点,直接在根节点的左右子树添加lazy标记就可以了,这样快了20ms...


#include<iostream>
#define MAXN 100005
using namespace std;

int col[MAXN<<2];
int sum[MAXN<<2];

int max( int a,int b ){ return a>b?a:b; }
int min( int a,int b ){ return a<b?a:b; }

void PushDown( int rt )
{
     if( col[rt] )
     {
         col[rt<<1]=col[rt<<1|1]=col[rt];
         sum[rt<<1]=sum[rt<<1|1]=sum[rt];
         col[rt]=0;
     }
}

void PushUp( int rt ){
     sum[rt]=sum[rt<<1]|sum[rt<<1|1];
}

void build( int l,int r,int rt )
{
     sum[rt<<1]=sum[rt<<1|1]=sum[rt]=1;
     col[rt<<1]=col[rt<<1|1]=1;
     PushUp(rt);
}

int getans( int x )
{
    int ans=0;
    while( x>0 )
    {
           ans+=x%2;
           x/=2;
    }
    return ans;
}

void update( int l,int r,int value,int L,int R,int rt )
{
     if( l<=L&&R<=r )
     {
         col[rt]=1;
         sum[rt]=1<<(value-1);
//         printf( "sum[%d]:%d",rt,sum[rt] );  
         return ;
     }
     PushDown( rt );
     int m=(R+L)>>1;
     if( l<=m ) update( l,r,value,L,m,rt<<1 );
     if( m<r ) update( l,r,value,m+1,R,rt<<1|1 );
     PushUp(rt);
}

int query( int l,int r,int L,int R,int rt )
{
     if( l<=L&&R<=r ){
         return sum[rt];
     }
     PushDown( rt );
     int m=(R+L)>>1;
     int a=0,b=0;
     if( l<=m ) a=query( l,r,L,m,rt<<1 );
     if( m<r ) b=query( l,r,m+1,R,rt<<1|1 );
     return a|b;
}
int main()
{
    int L,T,O;
    while( scanf( "%d %d %d",&L,&T,&O )!=EOF )
    {
           build(1,L,1);
           char com;
           int a,b,c,i;
           for( i=1;i<=O;i++ )
           {
                scanf( "\n%c",&com );
                if( com=='C' )
                {
                    scanf( "%d %d %d",&a,&b,&c );
                    update( min(a,b),max(a,b),c,1,L,1);
                }
                else
                {
                    scanf( "%d %d",&a,&b );
                    printf( "%d\n",getans(query(min(a,b),max(a,b),1,L,1)) );
                }
           }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值