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