HDU 3397 Sequence operation (区间合并 )

    刚开始学习线段树,没想到就碰到这样的题,由于太弱,搞了整整一天和一早上,真是充满了血与泪,但起码是自己一点点写出来的,值得。另外,由此题注意到写数据结构的题,代码的风格重要,这方面也要下点功夫。因为一个细节DEBUG花了好久,数据有些繁琐,细节比较多。下面是代码。

/*
结点需要保存的变量有lmax1,rmax1,sum1,mx1.分别代表从左数最大连续1的个数,从右数最大连续1的个数,1的总个数,期中最大的1连续个数.
					lmax0....同理
mode 代表延迟标记的状态,这里我定义0:无,1:操作0,2:操作1,3:操作2.		
L,R则代表结点区间范围。	
*/
#include <iostream>
using namespace std;
int data[200000];
struct node{
	int L,R;
	int lmax1,rmax1,sum1,mx1;
	int lmax0,rmax0,sum0,mx0;
	int mode;
	int dis(){return  R-L+1;}	//节点的区间大小
	int mid(){return (L+R)/2;}	//取中
	void set_1(){lmax1=dis(),rmax1=dis(),sum1=dis(),mx1=dis(),lmax0=0,rmax0=0,sum0=0,mx0=0;}	//操作1
	void set_0(){lmax0=dis(),rmax0=dis(),sum0=dis(),mx0=dis(),lmax1=0,rmax1=0,sum1=0,mx1=0;}	//操作0
	void change(){swap(lmax0,lmax1),swap(rmax0,rmax1),swap(mx1,mx0),swap(sum0,sum1);}	//操作2 :交换0和1的数据即可
};
node K[4*200000];
void push(int rt){	//向上更新操作
	int q=rt<<1;
	int p=q+1;
	K[rt].lmax1=K[q].lmax1; K[rt].rmax1=K[p].rmax1;
	K[rt].lmax0=K[q].lmax0;	K[rt].rmax0=K[p].rmax0;
	K[rt].sum0=K[q].sum0+K[p].sum0;
	K[rt].sum1=K[q].sum1+K[p].sum1;
	K[rt].mx1=max(max(K[q].mx1,K[p].mx1),K[q].rmax1+K[p].lmax1);		//这里以下是能够区间合并时需要更新的数据
	K[rt].mx0=max(max(K[q].mx0,K[p].mx0),K[q].rmax0+K[p].lmax0);
	if(K[q].lmax0==K[q].dis()) K[rt].lmax0=K[q].dis()+K[p].lmax0;	
	if(K[p].rmax0==K[p].dis()) K[rt].rmax0=K[p].dis()+K[q].rmax0;
	if(K[q].lmax1==K[q].dis()) K[rt].lmax1=K[q].dis()+K[p].lmax1;
	if(K[p].rmax1==K[p].dis()) K[rt].rmax1=K[p].dis()+K[q].rmax1;
	return ;		 
}
void build(int l,int r,int rt){
	K[rt].mode=0;
	K[rt].L=l;
	K[rt].R=r;
	if(l==r){
		if(data[l]==0){K[rt].set_0();}
		else{K[rt].set_1();}
		return;
	}
	int m=K[rt].mid();
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1);
	push(rt);
	return ;
}
void trans(int transmode,int rt){		//操作函数
	if(transmode==1){
		K[rt].set_0();
	}
	else if(transmode==2){
		K[rt].set_1();
	}
	else if(transmode==3){
		K[rt].change();
	}
	return;
}
void modechan(int son,int fa){
	if(K[fa].mode==3){
		K[son].mode=K[fa].mode-K[son].mode;//3-3=0,3-1=2,3-2=1 因此正好可以用一个减法。(或者可以用两个延迟标记来区分操作0,1与操作2)
	}
	else
		K[son].mode=K[fa].mode;
	return ;
}
void updata(int M,int L,int R,int rt){	//更新操作
	if(K[rt].L>=L&&K[rt].R<=R)
	{
			if(M==2&&K[rt].mode){			//这里表示在原来有延迟标记并且要赋予操作2的情况该如何转化
				trans(M+1,rt);		//先把该结点更新
				K[rt].mode=M/K[rt].mode;		
			}
			else{					//原来没有延迟标记或要赋予的不是操作2,直接覆盖。
				trans(M+1,rt);
				K[rt].mode=M+1;
			}
		return ;
	}
	if(K[rt].mode){
		trans(K[rt].mode,rt<<1);
		trans(K[rt].mode,rt<<1|1);
		modechan(rt<<1,rt);
		modechan(rt<<1|1,rt);
		K[rt].mode=0;
	}
	int m=K[rt].mid();
	if(R<=m)
		updata(M,L,R,rt<<1);
	else if(L>m)
		updata(M,L,R,rt<<1|1);
	else
	{
		updata(M,L,R,rt<<1);
		updata(M,L,R,rt<<1|1);
	}
	push(rt);
	return;
}

int query(int M,int L,int R,int rt){
	if(K[rt].L>=L&&K[rt].R<=R){
		if(M==3){
			return K[rt].sum1;
		}
		else
			return K[rt].mx1;
	}
	if(K[rt].mode){
		trans(K[rt].mode,rt<<1);
		trans(K[rt].mode,rt<<1|1);
		modechan(rt<<1,rt);
		modechan(rt<<1|1,rt);	
		K[rt].mode=0;
	}
	int m=K[rt].mid();
	if(R<=m)
		return query(M,L,R,rt<<1);
	else if(L>m)
		return query(M,L,R,rt<<1|1);
	else{
		int k1=query(M,L,R,rt<<1);
		int k2=query(M,L,R,rt<<1|1);
		if(M==4){
				int s=min(K[rt<<1].rmax1,m-L+1)+min(K[rt<<1|1].lmax1,R-m);
				return max(s,max(k1,k2));
		}
		else return k1+k2;
	}
}


int main(){
	int t,n,m;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
			scanf("%d",&data[i]);
		build(1,n,1);
		int G,L,R;
		for(int i=1;i<=m;i++){
			scanf("%d%d%d",&G,&L,&R);	//这里我定义的区间是[1,n] 所以在给的数据上要+1
			if(G==3||G==4)
				printf("%d\n",query(G,L+1,R+1,1));
			else
				updata(G,L+1,R+1,1);
		}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值