NZAU 1207: Candies 线段树区间更新加区间合并

题意:

给你一数组,数组的值只有0(A糖果)和1(B糖果)两种,对这个数组有两种操作:(1)把区间L到R的值更改为v, (2)询问从区间L到R最多有多少个1是连续的。

线段树维护三个值  pre,sub,suf  分别代表前缀,子串,后缀连续为1的个数,

三个值的更新方式为下图,很好理解

void pushup(Node &rt,Node a,Node b,int l,int r){
	int mid=(l+r)/2;
	if(a.pre==mid-l+1){
		rt.pre=a.pre+b.pre;
	}else rt.pre=a.pre;
	if(b.suf==r-mid){
		rt.suf=b.suf+a.suf;
	}else rt.suf=b.suf;
	rt.sub=max(a.suf+b.pre,max(a.sub,b.sub));
}

lazy的标记在build的时候初始化,向下更新的时候,左右孩子的三个值也要维护,当前节点设置标记时,也要维护这三个值。

query的时候,根据查询范围,来分类,R<=mid 说明要在左子树查询,L>=mid+1,在右子树,其他的就是mid在L和R的中间。

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

typedef long long ll;
const ll INF=1e15+10;
const int maxn=1e6+5;
char s[1000006];
int cnt;
struct Node{
	int pre,suf,sub,lazy;
}node[maxn*4];
void pushup(Node &rt,Node a,Node b,int l,int r){
	int mid=(l+r)/2;
	if(a.pre==mid-l+1){
		rt.pre=a.pre+b.pre;
	}else rt.pre=a.pre;
	if(b.suf==r-mid){
		rt.suf=b.suf+a.suf;
	}else rt.suf=b.suf;
	rt.sub=max(a.suf+b.pre,max(a.sub,b.sub));
}

void pushdown(Node &rt,Node &a,Node &b,int l,int r){
	if(rt.lazy!=-1){
		a.lazy=b.lazy=rt.lazy;
		if(rt.lazy==0)
		a.pre=a.sub=a.suf=b.pre=b.sub=b.suf=0;
		else{
			int mid=(l+r)/2;
			a.pre=a.sub=a.suf=mid-l+1;
			b.pre=b.sub=b.suf=r-mid;
		}
		rt.lazy=-1;
	}
}
void build(int l,int r,int rt){
	node[rt].lazy=-1;
	if(l==r){
		Node &t=node[rt];
		if(s[cnt++]-'A'==1)
		t.sub=t.pre=t.suf=1;
		else t.sub=t.pre=t.suf=0;
		return;
	}
	int mid=(l+r)/2;
	build(l,mid,rt*2);
	build(mid+1,r,rt*2+1);
	pushup(node[rt],node[rt*2],node[rt*2+1],l,r);
}

void update(int L,int R,int k,int l,int r,int rt){
	if(L<=l&&r<=R){
		node[rt].lazy=k;
		if(k==0)
		node[rt].sub=node[rt].suf=node[rt].pre=0;
		else node[rt].sub=node[rt].suf=node[rt].pre=r-l+1;
		return;
	}
	int mid=(l+r)/2;
	pushdown(node[rt],node[rt*2],node[rt*2+1],l,r);
	if(L<=mid) update(L,R,k,l,mid,rt*2);
	if(R>=mid+1) update(L,R,k,mid+1,r,rt*2+1);
	pushup(node[rt],node[rt*2],node[rt*2+1],l,r);
}

Node query(int L,int R,int l,int r,int rt){
	if(L<=l&&r<=R){
		return node[rt]; 
	}
	pushdown(node[rt],node[rt*2],node[rt*2+1],l,r);
	int mid=(l+r)/2;
	Node ans;	
	if(R<=mid) return query(L,R,l,mid,rt*2);
	else if(L>=mid+1) return query(L,R,mid+1,r,rt*2+1);
	else pushup(ans,query(L,R,l,mid,rt*2),query(L,R,mid+1,r,rt*2+1),l,r);
	return ans;
}
int main(){
	//freopen("out.txt","w",stdout);
	int T,cas=1;
	scanf("%d",&T);
	while(T--){
		printf("Case #%d:\n",cas++);
		int n,m;
		cnt=0;
		scanf("%d %d",&n,&m);
		scanf("%s",s);
		build(1,n,1);
		while(m--){
			int op,l,r,k;
			scanf("%d",&op);
			if(op==1){
				scanf("%d %d %d",&l,&r,&k);
				k--;
				update(l,r,k,1,n,1);
			}else{
				scanf("%d %d",&l,&r);
				Node ans=query(l,r,1,n,1);
				printf("%d\n",ans.sub);
			}
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值