码迷,mamicode.com
首页 > 其他好文 > 详细

hdu3397 Sequence operation

时间:2015-05-30 16:48:47      阅读:126      评论:0      收藏:0      [点我收藏+]

标签:线段树

Problem Description
lxhgww got a sequence contains n characters which are all ‘0‘s or ‘1‘s.
We have five operations here:
Change operations:
0 a b change all characters into ‘0‘s in [a , b]
1 a b change all characters into ‘1‘s in [a , b]
2 a b change all ‘0‘s into ‘1‘s and change all ‘1‘s into ‘0‘s in [a, b]
Output operations:
3 a b output the number of ‘1‘s in [a, b]
4 a b output the length of the longest continuous ‘1‘ string in [a , b]
 

Input
T(T<=10) in the first line is the case number.
Each case has two integers in the first line: n and m (1 <= n , m <= 100000).
The next line contains n characters, ‘0‘ or ‘1‘ separated by spaces.
Then m lines are the operations:
op a b: 0 <= op <= 4 , 0 <= a <= b < n.
 

Output
For each output operation , output the result.
 

Sample Input
1 10 10 0 0 0 1 1 0 1 0 1 1 1 0 2 3 0 5 2 2 2 4 0 4 0 3 6 2 3 7 4 2 8 1 0 5 0 5 6 3 3 9
 

Sample Output
5 2 6

5

这道题是前面线段树知识的综合题,也和之前有些不同,是个很好的训练题,做了很长时间啊,还需努力。这里的难点是异或运算,即把区间中的0变成1,1变成0.这里我维护每个区间的llen0,rlen0,tlen0,llen1,rlen1,tlen1,即区间从左端点起的向右最大连续0(1)区间长度,从右端起的最大向左连续0(1)区间长度,以及区间最大的连续0(1)区间长度,然后异或操作的时候,只要把0,1换一下,然后用pushup操作就可以了,再记录一个异或操作符x_or.

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
#define maxn 100005
int a[maxn];
struct node{
	int l,r,sum,color,x_or;
	int llen0,rlen0,tlen0;
	int llen1,rlen1,tlen1;
}b[4*maxn];
int max(int a,int b){
	return a>b?a:b;
}
int min(int a,int b){
	return a<b?a:b;
}

void XOR(int i)
{
	int mid;
	swap(b[i].llen0,b[i].llen1);
	swap(b[i].rlen0,b[i].rlen1);
	swap(b[i].tlen0,b[i].tlen1);
	b[i].sum=b[i].r-b[i].l+1-b[i].sum;
}
void pushup(int i)
{
	if(b[i].l==b[i].r)return;
	b[i].tlen0=max(b[i*2].tlen0,b[i*2+1].tlen0);
	b[i].tlen0=max(b[i].tlen0,b[i*2].rlen0+b[i*2+1].llen0);
	
	b[i].llen0=b[i*2].llen0;b[i].rlen0=b[i*2+1].rlen0;
	if(b[i*2].llen0==b[i*2].r-b[i*2].l+1)b[i].llen0+=b[i*2+1].llen0;
	if(b[i*2+1].rlen0==b[i*2+1].r-b[i*2+1].l+1)b[i].rlen0+=b[i*2].rlen0;
	
	b[i].tlen1=max(b[i*2].tlen1,b[i*2+1].tlen1);
	b[i].tlen1=max(b[i].tlen1,b[i*2].rlen1+b[i*2+1].llen1);
	
	b[i].llen1=b[i*2].llen1;b[i].rlen1=b[i*2+1].rlen1;
	if(b[i*2].llen1==b[i*2].r-b[i*2].l+1)b[i].llen1+=b[i*2+1].llen1;
	if(b[i*2+1].rlen1==b[i*2+1].r-b[i*2+1].l+1)b[i].rlen1+=b[i*2].rlen1;
	
	b[i].sum=b[i*2].sum+b[i*2+1].sum;
}
void build(int l,int r,int i)
{
	int mid;
	b[i].l=l;b[i].r=r;b[i].color=-1;b[i].x_or=0;
	if(l==r){
		b[i].llen0=b[i].rlen0=b[i].tlen0=a[l]?0:1;
		b[i].llen1=b[i].rlen1=b[i].tlen1=a[l]?1:0;
		b[i].color=a[l];
		b[i].sum=a[l];return;
	}
	mid=(l+r)/2;
	build(l,mid,i*2);
	build(mid+1,r,i*2+1);
	pushup(i);
}

void pushdown(int i)
{
	int mid;
	if(b[i].l==b[i].r)return;
	if(b[i].color!=-1){   //如果是纯色,那么需要下传 
		b[i*2].color=b[i*2+1].color=b[i].color;
		b[i*2].x_or=b[i*2+1].x_or=0; //这里因为左右子树的值和父亲一样,所以如果父亲是纯色且翻转一次,那么传到左右子树也是一样的,不管子树记录多少次(先记为0,再后面一步变成1) 
		
		b[i*2].llen0=b[i*2].rlen0=b[i*2].tlen0=b[i].color?0:(b[i*2].r-b[i*2].l+1);
		b[i*2].llen1=b[i*2].rlen1=b[i*2].tlen1=b[i].color?(b[i*2].r-b[i*2].l+1):0;
		b[i*2].sum=b[i].color?(b[i*2].r-b[i*2].l+1):0;
		
		b[i*2+1].llen0=b[i*2+1].rlen0=b[i*2+1].tlen0=b[i].color?0:(b[i*2+1].r-b[i*2+1].l+1);
		b[i*2+1].llen1=b[i*2+1].rlen1=b[i*2+1].tlen1=b[i].color?(b[i*2+1].r-b[i*2+1].l+1):0;
		b[i*2+1].sum=b[i].color?(b[i*2+1].r-b[i*2+1].l+1):0;
		b[i].color=-1;
	}
	if(b[i].x_or){
		b[i*2].x_or^=1;
		b[i*2+1].x_or^=1;
		b[i].x_or=0;
		XOR(i*2);
		XOR(i*2+1);
	}
}

void update(int l,int r,int flag,int i)
{
	int mid;
	//if(b[i].color==flag)return;  这里和之前做过的染色题不同,颜色相同不能直接返回,因为可能这段上有翻转标记,比如整段区间都是0,但是x_or=1,那么其实这段是1,当flag=0时是要更新的,如果返回就错了。 
	pushdown(i); //这里也和之前的题目不同,之前都是在第一个if下面写的,这里提前写是因为要把翻转信息提前传给子树,如果没有的话,可能下面第一个if中翻转标记就消失了,那么子树就没有得到翻转信息。 
	if(b[i].l==l && b[i].r==r){
		if(flag<2){
			b[i].color=flag;
			b[i].x_or=0;
			b[i].llen0=b[i].rlen0=b[i].tlen0=flag?0:(b[i].r-b[i].l+1);
			b[i].llen1=b[i].rlen1=b[i].tlen1=flag?(b[i].r-b[i].l+1):0;
			b[i].sum=flag?(b[i].r-b[i].l+1):0;
		}
		else{
			b[i].x_or=1;
			XOR(i);
		}
		return;
	}
	
	mid=(b[i].l+b[i].r)/2;
	if(r<=mid)update(l,r,flag,i*2);
	else if(l>mid)update(l,r,flag,i*2+1);
	else {
		update(l,mid,flag,i*2);
		update(mid+1,r,flag,i*2+1);
	}
	pushup(i);
}


int question(int l,int r,int flag,int i)
{
	int mid,r1,l1,ans1,ans2;
	if(b[i].l==l && b[i].r==r){
		if(flag==3)return b[i].sum;
		else return b[i].tlen1;
	}
	pushdown(i);  //询问操作可以在if后面的 
	mid=(b[i].l+b[i].r)/2;
	if(r<=mid) return question(l,r,flag,i*2);
	else if(l>mid)return question(l,r,flag,i*2+1);
	if(flag==3)return question(l,mid,flag,i*2)+question(mid+1,r,flag,i*2+1);
	
	if(b[i*2+1].l+b[i*2+1].llen1-1>r)r1=r;
	else r1=b[i*2+1].l+b[i*2+1].llen1-1;
	
	if(b[i*2].r-b[i*2].rlen1+1<l)l1=l;
	else l1=b[i*2].r-b[i*2].rlen1+1;
	
	ans1=r1-l1+1;
	ans2=max(question(l,mid,flag,i*2),question(mid+1,r,flag,i*2+1));
	return max(ans1,ans2);
}

int main()
{
	int n,m,i,j,T,flag,c,d;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		build(1,n,1);
		for(i=1;i<=m;i++){
			scanf("%d%d%d",&flag,&c,&d);
			c++;d++;
			if(flag<=2){
				update(c,d,flag,1);
			}
			else printf("%d\n",question(c,d,flag,1));
		}
	}
	return 0;
}


hdu3397 Sequence operation

标签:线段树

原文地址:http://blog.csdn.net/kirito_acmer/article/details/46274571

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!