hdu3397 Sequence operation

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;
}

时间: 2024-10-11 10:14:51

hdu3397 Sequence operation的相关文章

hdu-3397 Sequence operation 线段树多种标记

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3397 题目大意: 0 a b表示a-b区间置为0 1 a b表示a-b区间置为1 2 a b表示a-b区间中的0变成1,1变成0 3 a b表示a-b区间中的1的数目 4 a b表示a-b区间中最长连续1的长度 解题思路: 线段树多种标记. 需要处理的东西比较多: 做题的时候发现一个问题: 我的宏定义Max不可以用于函数,尤其是递归函数,这样会使得同一函数重复调用好几遍,递归函数的话更会超时. 1

HDU 3397 Sequence operation(线段树)

HDU 3397 Sequence operation 题目链接 题意:给定一个01序列,有5种操作 0 a b [a.b]区间置为0 1 a b [a,b]区间置为1 2 a b [a,b]区间0变成1,1变成0 3 a b 查询[a,b]区间1的个数 4 a b 查询[a,b]区间连续1最长的长度 思路:线段树线段合并.须要两个延迟标记一个置为01,一个翻转,然后因为4操作,须要记录左边最长0.1.右边最长0.1,区间最长0.1,然后区间合并去搞就可以 代码: #include <cstdi

hdu 3379 Sequence operation(成段更新,区间合并)

http://acm.hdu.edu.cn/showproblem.php?pid=3397 线段树很好的题.涉及到的知识点:lazy操作处理异或操作和置01,区间合并. 有五种操作: 0 a b 将[a,b]变为0 1 a b 将[a,b]变为1 2 a b 将[a,b]取反 3 a b 输出[a,b]的1的个数 4 a b 输出[a,b]内最长的连续1的个数 对区间的操作与poj 3225类似.置01与取反是互斥的,一段区间上只能有一个标记,discuss里面也说了取反的时候若递归到区间全为

Sequence operation(线段树区间多种操作)

Sequence operation Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 7452    Accepted Submission(s): 2220 Problem Description lxhgww got a sequence contains n characters which are all '0's or '1

HDU 3397——Sequence operation(线段树,区间染色+区间异或+区间合并)

Sequence operation Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 6270    Accepted Submission(s): 1862 Problem Description lxhgww got a sequence contains n characters which are all '0's or '1

HDU 3397 Sequence operation(线段树&#183;成段更新&#183;区间合并&#183;混合操作)

题意  给你一个只有0, 1的数组  有这些操作 0. 将[a, b]区间的所有数都改为0 1. 将[a, b]区间的所有数都改为1 2. 将[a, b]区间的所有数都取反 即与1异或 3. 输出区间[a, b]中1的个数  即所有数的和 4. 输出区间[a, b]中最大连续1的长度 对于所有的3, 4操作输出对应的答案 单个的操作都很简单  但搞在一起就有点恶心了  还好数组里的数只有0和1 线段树维护9个值 对应区间0, 1的最大长度len[i]  对应区间左端点为起点的最大0, 1长度ll

【线段树】HDU 3397 Sequence operation 区间合并

操作 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,

HDU 3397 Sequence operation 线段树

线段树大杂烩~ 各种操作都有,细心点不难1A #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; #define lson rt<<1,l,mid #define rson rt<<1|1,mid + 1,r const int maxn = 1e5 + 10; int lmax[maxn

HDU 3397 Sequence operation (线段树,成段更新,区间合并)

http://acm.hdu.edu.cn/showproblem.php?pid=3397 Sequence operation Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5801    Accepted Submission(s): 1713 Problem Description lxhgww got a sequence