2011多校八1002 hdu3911一段区间内的数取反与查询

本文介绍了一种使用线段树解决区间翻转及查询最长连续1的问题的方法。通过递归构建和更新线段树,实现对区间进行翻转操作,并能高效查询区间内最长连续1的数量。

        题意:给你N个数(每个数不是0,就是1),有两个操作,将一段区间的数取反,查询一段区间连续1的个数。

        分析:真是太水了,这题总是想不太清,好像递归的我还是不怎么在行。过了的想法是,先根据初始状态建立线段树,然后对于取反,从上往下,如果某一段是在前面有奇数次整段取反的,那么现在一分为二,分给两个子代。必须保证前面都是最终的状态(这样如果查找是这整个区间就可以直接用)。对于查找也是一样的,如果碰到上面的依然是划分到子区间去。而连续最多的1,有三部分组成,左区间,右区间或者中间。注意求中间的时候可能,连续的1数目比你查找的区间的还要多。。。

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;

struct tree
{
	int l,r,l0,r0,l1,r1,m0,m1;
	bool tag;
}T[8100000];

int n,result,a[110000];

void CF(int s)//由两子代更新此结点信息
{
	T[s].m0=T[2*s].m0;
	if(T[s].m0<T[2*s+1].m0)
		T[s].m0=T[2*s+1].m0;
	if(T[s].m0<T[2*s].r0+T[2*s+1].l0)
		T[s].m0=T[2*s].r0+T[2*s+1].l0;

	T[s].m1=T[2*s].m1;
	if(T[s].m1<T[2*s+1].m1)
		T[s].m1=T[2*s+1].m1;
	if(T[s].m1<T[2*s].r1+T[2*s+1].l1)
		T[s].m1=T[2*s].r1+T[2*s+1].l1;

	T[s].l0=T[2*s].l0;
	if(T[2*s].l0==T[2*s].r-T[2*s].l+1)
		T[s].l0+=T[2*s+1].l0;
	T[s].l1=T[2*s].l1;
	if(T[2*s].l1==T[2*s].r-T[2*s].l+1)
		T[s].l1+=T[2*s+1].l1;
	
	T[s].r0=T[2*s+1].r0;
	if(T[2*s+1].r0==T[2*s+1].r-T[2*s+1].l+1)
		T[s].r0+=T[2*s].r0;
	T[s].r1=T[2*s+1].r1;
	if(T[2*s+1].r1==T[2*s+1].r-T[2*s+1].l+1)
		T[s].r1+=T[2*s].r1;
}
void Build(int l,int r,int s)//根据初始信息建立线段树
{	
	T[s].l=l,T[s].r=r,T[s].tag=false;
	if(l>=r)
	{
		if(a[l]==0)
		{
			T[s].m0=1,T[s].m1=0;
			T[s].l0=1,T[s].l1=0;
			T[s].r0=1,T[s].r1=0;
		}
		else
		{
			T[s].m0=0,T[s].m1=1;
			T[s].l0=0,T[s].l1=1;
			T[s].r0=0,T[s].r1=1;
		}
		return;
	}
	int mid=(l+r)/2;
	Build(l,mid,s*2);
	Build(mid+1,r,s*2+1);

	CF(s);//根据子区间更新本身
}
void Swap(int s)//将s取反,即所有1,0交换
{
	swap(T[s].l0,T[s].l1);
	swap(T[s].r0,T[s].r1);
	swap(T[s].m0,T[s].m1);
}
void update(int l,int r,int s)
{
	if(l>T[s].r||r<T[s].l) return;
	if(l<=T[s].l&&r>=T[s].r)
	{
		T[s].tag=!T[s].tag;
		Swap(s);
		return;
	}
	if(T[s].tag)//前面有奇数次整段取反
	{
		T[s].tag=false;
		T[2*s].tag=!T[2*s].tag;
		T[2*s+1].tag=!T[2*s+1].tag;
		Swap(2*s);
		Swap(2*s+1);
	}

	update(l,r,2*s);
	update(l,r,2*s+1);

	CF(s);
}

void search(int l,int r,int s)
{
	if(l>T[s].r||r<T[s].l) return;
	if(l<=T[s].l&&r>=T[s].r)
	{
		if(T[s].m1>result)
			result=T[s].m1;
		return;
	}
	if(T[s].tag)
	{
		T[s].tag=false;
		T[2*s].tag=!T[2*s].tag;
		T[2*s+1].tag=!T[2*s+1].tag;
		Swap(2*s);
		Swap(2*s+1);
	}
	search(l,r,2*s);
	search(l,r,2*s+1);

	int i,j;
	i=T[2*s].r1;//左区间有连续1的数目
	if(i>T[2*s].r-l+1) i=T[2*s].r-l+1;//注意不能多于在左区间的长数
	j=T[2*s+1].l1;
	if(j>r-T[2*s+1].l+1) j=r-T[2*s+1].l+1;
	if(i+j>result) result=i+j;
}
int main()
{
	int i,m,u,l,r;
	while(scanf("%d",&n)!=EOF)
	{
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		Build(1,n,1);
		scanf("%d",&m);
		for(i=0;i<m;i++)
		{
			scanf("%d%d%d",&u,&l,&r);
			if(u)
			{
				update(l,r,1);
			}
			else
			{
				result=0;
				search(l,r,1);
				printf("%d\n",result);
			}
		}
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值