HDU 3397 Sequence operation(线段树)

本文深入探讨了线段树的基本操作、复杂问题解决策略,包括区间和、区间最值、区间翻转等,详细介绍了如何使用线段树解决实际问题,并提供了具体的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

见过最恶心的一个线段树了。

先总结一下最近做的线段树吧。(基本都是裸的╮(╯▽╰)╭)

线段树的操作就是单点更新,区间更新,区间查询。

最基本的就是区间和,区间最值。

一般都是10W个节点,10W个询问

比较难一点的就是区间翻转,区间内的各种连续。

对付区间翻转就是弄一个翻转标记rev[],rev[i]=1的话表示这个节点的子节点全都要翻转,如果有覆盖标记的话,要保持翻转标记和覆盖标记最多同时存在一个。

区间连续的话,比如说,求区间连续1的个数最多是多少,那就搞3个数组,mx[],lx[],rx[],mx[i]表示节点i里面连续1的个数的最大值,lx[i]表示从节点i左端点开始连续1的个数。

rx[i]表示右端点开始连续1的个数(对这个题,因为有翻转操作,所以还要知道连续0的个数)。然后更新的时候mx[],lx[],rx[]都要更新

线段树更难一点的就是树套树什么的了。。

当然,更恶心的是线段树的题你不知道要用线段树做!!


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<cstdlib>
#include<stack>
using namespace std;

#define inf 0x3f3f3f3f
#define eps 1e-7
#define LL long long
#define ULL unsigned long long
#define MP make_pair
#define pb push_back
#define ls ( i << 1 )
#define rs ( ls | 1 )
#define md ( ( ll[i] + rr[i] ) >> 1 )
#define PI acos( -1.0 )
#define mxn 400020

int ll[mxn], rr[mxn], add[mxn], rev[mxn], mx0[mxn], mx1[mxn], sum[mxn];
int lx0[mxn], lx1[mxn], rx0[mxn], rx1[mxn];

//ll[i],rr[i]节点i所管理的左端点和右端点
//add[i],覆盖标记,不存在为-1,0的覆盖标记为0,1的覆盖标记为1
//rev[i], 翻转标记
//mx0[i], 连续0最大个数,mx1[i],连续1的最大个数,sum[i]区间和
//lx0[i], 左端点开始连续0的个数,lx1[i],左端点开始连续1的个数
//rx0[i], rx1[i]类似

int a[mxn];
int n, m;

int len( int i ) { //返回区间长度
	return rr[i] - ll[i] + 1;
}
void Up( int i ) { //向上更新,就是通过左儿子和右儿子更新自己
	sum[i] = sum[ls] + sum[rs];
	mx0[i] = max( mx0[ls], mx0[rs] );
	mx1[i] = max( mx1[ls], mx1[rs] );
	bool flag1 = rx1[ls], flag2 = lx1[rs];
	if( flag1 && flag2 ) {
		mx1[i] = max( mx1[i], rx1[ls] + lx1[rs] );//
		if( lx1[ls] == len( ls ) )
			lx1[i] = len( ls ) + lx1[rs];
		else
			lx1[i] = lx1[ls];
		if( rx1[rs] == len( rs ) )
			rx1[i] = len( rs ) + rx1[ls];
		else
			rx1[i] = rx1[rs];
		lx0[i] = lx0[ls];
		rx0[i] = rx0[rs];
		return;//
	}
	if( !flag1 && !flag2 ) {
		mx0[i] = max( mx0[i], rx0[ls] + lx0[rs] );
		if( lx0[ls] == len( ls ) )
			lx0[i] = len( ls ) + lx0[rs];
		else
			lx0[i] = lx0[ls];
		if( rx0[rs] == len( rs ) )
			rx0[i] = len( rs ) + rx0[ls];
		else
			rx0[i] = rx0[rs];
		lx1[i] = lx1[ls];
		rx1[i] = rx1[rs];
		return;
	}
	lx0[i] = lx0[ls];
	rx0[i] = rx0[rs];
	lx1[i] = lx1[ls];
	rx1[i] = rx1[rs];
}//

void ad( int i, int d ) { //把区间覆盖为d
	add[i] = d, rev[i] = 0;
	sum[i] = d * len( i );
	if( d ) {
		mx1[i] = rx1[i] = lx1[i] = sum[i];
		mx0[i] = rx0[i] = lx0[i] = 0;
	}
	else {
		mx1[i] = rx1[i] = lx1[i] = 0;
		mx0[i] = rx0[i] = lx0[i] = len( i );
	}
}//

void re( int i ) {//区间翻转
	if( add[i] != -1 ) {
		add[i] ^= 1;
		ad( i, add[i] );
		return;
	}
	rev[i] ^= 1;
	sum[i] = len( i ) - sum[i];
	int tem1 = mx1[i];
	int tem2 = lx1[i];
	int tem3 = rx1[i];
	mx1[i] = mx0[i];
	lx1[i] = lx0[i];
	rx1[i] = rx0[i];
	mx0[i] = tem1, lx0[i] = tem2, rx0[i] = tem3;
}
void Down( int i ) { //向下释放标记
	if( add[i] != -1 ) {
		ad( ls, add[i] ), ad( rs, add[i] );
		add[ls] = add[rs] = add[i];
		add[i] = -1;
		return;
	}
	if( rev[i] ) {
		re( ls ), re( rs );
		rev[i] = 0;
	}
}
void build( int l, int r, int i ) {
	add[i] = -1, rev[i] = 0;
	ll[i] = l, rr[i] = r;
	if( l == r ) {
		if( a[l] == 1 ) {
			sum[i] = 1;
			mx1[i] = lx1[i] = rx1[i] = 1;
			lx0[i] = rx0[i] = mx0[i] = 0;
		}
		else {
			sum[i] = 0;
			mx1[i] = lx1[i] = rx1[i] = 0;
			lx0[i] = rx0[i] = mx0[i] = 1;
		}
		return;
	}
	build( l, md, ls );
	build( md + 1, r, rs );
	Up( i );
}
void update( int l, int r, int v, int i ) {
	if( ll[i] == l && rr[i] == r ) {
		add[i] = v;
		ad( i, v );
		return;
	}
	Down( i );
	if( r <= md )
		update( l, r, v, ls );
	else
		if( l > md )
			update( l, r, v, rs );
		else
			update( l, md, v, ls ), update( md + 1, r, v, rs );
	Up( i );
}
void reve( int l, int r, int i ) {
	if( ll[i] == l && rr[i] == r ) {
		re( i );
		return;
	}
	Down( i );
	if( r <= md )
		reve( l, r, ls );
	else
		if( l > md )
			reve( l, r, rs );
		else
			reve( l, md, ls ), reve( md + 1, r, rs );
	Up( i );
}
int qsum( int l, int r, int i ) {
	if( ll[i] == l && rr[i] == r )
		return sum[i];
	Down( i );
	if( r <= md )
		return qsum( l, r, ls );
	if( l > md )
		return qsum( l, r, rs );
	return qsum( l, md, ls ) + qsum( md + 1, r, rs );
}
int q1( int l, int r, int i ) { //查询连续1的个数的最大值
	if( ll[i] == l && rr[i] == r )
		return mx1[i];
	Down( i );
	if( r <= md )
		return q1( l, r, ls );
	if( l > md )
		return q1( l, r, rs );
	int ret = max( q1( l, md, ls ), q1( md + 1, r, rs ) );
	if( rx1[ls] && lx1[rs] ) {//如果区间中点两边都是1的话最大值可能就是在区间的中间取得
		int head = max( l, rr[ls] - rx1[ls] + 1 );
		int tail = min( r, ll[rs] + lx1[rs] - 1 );
		ret = max( ret, tail - head + 1 );
	}
	return ret;
}
void read() {
	scanf( "%d%d", &n, &m );
	for( int i = 1; i <= n; ++i )
		scanf( "%d", &a[i] );
}
int main() {
//	freopen( "tt.txt", "r", stdin );
	int cas;
	scanf( "%d", &cas );
	while( cas-- ) {
		read();
		int op, l, r;
		build( 1, n, 1 );
		while( m-- ) {
			scanf( "%d%d%d", &op, &l, &r );
			l++, r++;
			if( op == 0 ) { 
				update( l, r, 0, 1 );
			}
			if( op == 1 )
				update( l, r, 1, 1 );
			if( op == 2 )
				reve( l, r, 1 );
			if( op == 3 ) {
				printf( "%d\n", qsum( l, r, 1 ) );
			}
			if( op == 4 )
				printf( "%d\n", q1( l, r, 1 ) );
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值