HDU 3911 Black And White(线段树区间合并)

本文介绍了一种基于段式树的数据结构实现,用于高效处理区间更新和查询操作。通过预处理和递归分解,算法能在对数时间内完成对数组中特定区间的元素属性更改,并快速求解该区间内的特定属性值,如最长连续黑色或白色序列长度。适用于需要频繁进行区间操作的场景。

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

题目链接

实现代码

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<vector>
#include<queue>
#include<string>
#include <cmath>
#define mod 999997
#define lson l, mid, root << 1
#define rson mid + 1, r, root << 1 | 1
#define father  l , r , root
#define lowbit(x) ( x & ( - x ) )
using namespace std;
typedef long long ll;
const int maxn = 100100;
const int inf = 0x3f3f3f3f;

int num[maxn];

struct node{
	int lbsum;//最右边黑色序列长度
	int lwsum;//最左边白的序列长度
	int rbsum;//最右边黑色序列长度
	int rwsum;//最右边白色序列长度
	int bsum;//区间最长黑色序列长度
	int wsum;//区间最长白色序列长度
	int lazy;
}line[maxn<<2];

void push_up(int root,int l,int r){

	int ql=root<<1;
	int qr=root<<1|1;
	int mid=(r+l)>>1;
	int sl=mid-l+1;//左区间长度
	int sr=r-mid;//右区间长度

	//黑色
    line[root].lbsum=line[ql].lbsum;
    //当前结点的lsum肯定等于左儿子的lsum
	line[root].rbsum=line[qr].rbsum;
    if(line[root].lbsum==sl)
        line[root].lbsum+=line[qr].lbsum;
    //如果左边的长度就等于左儿子区间长度那么肯定就可以加上右儿子的lsum
    if(line[root].rbsum==sr)
        line[root].rbsum+=line[ql].rbsum;//特别注意区间不要写反了

    line[root].bsum=max(max(line[ql].bsum,line[qr].bsum),line[ql].rbsum+line[qr].lbsum);
    //当前结点的最大的连续sum1等于左儿子,右儿子和他们中间的最长连续1的长度。
    //注意这里是提醒自己,不能直接用父亲的左最长连续1和右最长连续1,因为他们可能不是最大的

    //白色
    line[root].lwsum=line[ql].lwsum;
    line[root].rwsum=line[qr].rwsum;
    if(line[root].lwsum==sl)
        line[root].lwsum+=line[qr].lwsum;
    if(line[root].rwsum==sr)
        line[root].rwsum+=line[ql].rwsum;

    line[root].wsum=max(max(line[ql].wsum,line[qr].wsum),line[ql].rwsum+line[qr].lwsum);

}
void SWAP(int root){

    swap(line[root].bsum,line[root].wsum);
    swap(line[root].lbsum,line[root].lwsum);
    swap(line[root].rbsum,line[root].rwsum);

}

void push_down(int root){

    if(line[root].lazy==0)return ;

    int ql=root<<1;
    int qr=root<<1|1;

    SWAP(ql);
    line[ql].lazy=!line[ql].lazy;
    SWAP(qr);
    line[qr].lazy=!line[qr].lazy;

    line[root].lazy=!line[root].lazy;

}

void change(int L,int R,int l,int r,int root){

    if(L<=l&&R>=r){
        SWAP(root);//这个时候直接把黑白互换一下即可
        line[root].lazy=!line[root].lazy;
        return ;
    }

    push_down(root);//向下更新

    int mid=(l+r)>>1;
    if(mid>=L)change(L,R,lson);
    if(mid<R)change(L,R,rson);
    push_up(root,l,r);//向上更新

}

void build_tree(int l,int r,int root){

		line[root].lazy=0;

	if(l==r){
        line[root].bsum=line[root].rbsum=line[root].lbsum=num[l];
        line[root].wsum=line[root].rwsum=line[root].lwsum=!num[l];
		return ;
	}

	int mid=(l+r)>>1;
	build_tree(lson);
	build_tree(rson);
	push_up(root,l,r);

}

int query(int L,int R,int l,int r,int root){
    if(L<=l&&R>=r){
        return line[root].bsum;
    }
    push_down(root);//向下更新
    int mid=(l+r)>>1;
    if(mid>=R)return query(L,R,lson);
    //查找的区间全在左边的时候直接返回(他的值只存在于这个区间里)
    if(mid<L)return query(L,R,rson);
    //同理查找的区间全在右边的时候直接返回

    //如果既不全在左边也不全在右边的话,就查找两边和中间的连续最大1
    int ls=query(L,R,lson);
    //左边最大的连续1的个数
    int rs=query(L,R,rson);
    //右边最大的连续1的个数

    int ll=line[root<<1].rbsum;
    if(ll>=mid-L+1)ll=mid-L+1;
    int rr=line[root<<1|1].lbsum;
    if(rr>=R-mid)rr=R-mid;
    //这里是为了保证在查询的区间内,因为ll或者rr的长度可能超过当前的查找区间

    return max(max(ls,rs),ll+rr);

}

int main(){
	int n;
	while(~scanf("%d",&n)){
		for(int i=1;i<=n;i++){
			scanf("%d",&num[i]);
		}
		build_tree(1,n,1);
		int q;
		scanf("%d",&q);
		while(q--){
			int flag,l,r;
			scanf("%d%d%d",&flag,&l,&r);
			if(flag){
				change(l,r,1,n,1);
			}else{
				printf("%d\n",query(l,r,1,n,1));
			}
		}
	}
    return 0;
}
/*
8
0 0 0 1 1  0 0 1
10
8
1 1 1 0 0 1 1 0
10
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值