【HDU】4973 A simple simulation problem. 线段树

传送门:【HDU】4973 A simple simulation problem.


题目分析:比赛的时候是队友写的,赛后自己重新写了一发。

本题只要我们给每个节点保存他的元素个数以及相同元素的最大数量即可。

每个区间元素个数翻倍的传递就用mul[]保存,记录翻倍的次数,最后乘以2^mul[]即可,2^mul[]可以预处理。

update注意因为是根据左儿子区间的元素个数来决定接下来的走向,如果两个儿子都要走上一遭,又因为先走左儿子会改变左儿子的元素个数,所以应该先走右边,然后再走左边。

其他就和普通的线段树一样了,我就因为没有发现先走左儿子会改变左儿子的元素个数,所以wa了无数发,还是造了数据才发现的。。。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define CLR( a , x ) memset ( a , x , sizeof a )
#define CPY( a , x ) memcpy ( a , x , sizeof a )

#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define rt o , l , r
#define root 1 , 1 , n
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )

typedef long long LL ;

const int MAXN = 50005 ;

LL sum[MAXN << 2] , cnt[MAXN << 2] ;
int mul[MAXN << 2] , pow[30] ;

void pushup ( int o ) {
	sum[o] = sum[ls] + sum[rs] ;
	cnt[o] = max ( cnt[ls] , cnt[rs] ) ;
}

void pushdown ( int o ) {
	if ( mul[o] ) {
		sum[ls] *= pow[mul[o]] ;
		sum[rs] *= pow[mul[o]] ;
		cnt[ls] *= pow[mul[o]] ;
		cnt[rs] *= pow[mul[o]] ;
		mul[ls] += mul[o] ;
		mul[rs] += mul[o] ;
		mul[o] = 0 ;
	}
}

void build ( int o , int l , int r ) {
	if ( l == r ) {
		sum[o] = cnt[o] = 1 ;
		return ;
	}
	int m = mid ;
	build ( lson ) ;
	build ( rson ) ;
	pushup ( o ) ;
}

void update ( LL L , LL R , int o , int l , int r ) {
	if ( l == r ) {
		cnt[o] = sum[o] += R - L + 1 ;
		return ;
	} else if ( R - L + 1 == sum[o] ) {
		sum[o] <<= 1 ;
		cnt[o] <<= 1 ;
		mul[o] ++ ;
		return ;
	}
	pushdown ( o ) ;
	int m = mid ;
	if ( R <= sum[ls] ) update ( L , R, lson ) ;
	else if ( sum[ls] < L ) update ( L - sum[ls] , R - sum[ls] , rson ) ;
	else {
		update ( 1 , R - sum[ls] , rson ) ;
		update ( L , sum[ls] , lson ) ;
	}
	pushup ( o ) ;
}

LL query ( LL L , LL R , int o , int l , int r ) {
	if ( l == r ) return R - L + 1 ;
	if ( R - L + 1 == sum[o] ) return cnt[o] ;
	pushdown ( o ) ;
	int m = mid ;
	if ( R <= sum[ls] ) return query ( L , R , lson ) ;
	if ( sum[ls] <  L ) return query ( L - sum[ls] , R - sum[ls] , rson ) ;
	return max ( query ( L , sum[ls] , lson ) , query ( 1 , R - sum[ls] , rson ) ) ;
}

void scanf ( LL& x , char c = 0 ) {
	while ( ( c = getchar () ) < '0' || c > '9' ) ;
	x = c - '0' ;
	while ( ( c = getchar () ) >= '0' && c <= '9' ) x = x * 10 + c - '0' ;
}

void solve () {
	int n , m ;
	char c ;
	LL L , R ;
	scanf ( "%d%d" , &n , &m ) ;
	build ( root ) ;
	CLR ( mul , 0 ) ;
	while ( m -- ) {
		c = getchar () ;
		scanf ( L ) , scanf ( R ) ;
		if ( c == 'D' ) update ( L , R , root ) ;
		else printf ( "%I64d\n" , query ( L , R , root ) ) ;
	}
}

int main () {
	int T , cas = 0 ;
	pow[0] = 1 ;
	REP ( i , 1 , 30 ) pow[i] = pow[i - 1] << 1 ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) {
		printf ( "Case #%d:\n" , ++ cas ) ;
		solve () ;
	}
	return 0 ;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值