bzoj1858:序列操作 (线段树区间信息合并)

1858: [Scoi2010]序列操作

Time Limit: 10 Sec  Memory Limit: 64 MB

Description

lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 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 对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?

Input

输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目 第二行包括n个数,表示序列的初始状态 接下来m行,每行3个数,op, a, b,(0<=op<=4,0<=a<=b<=""div="" style="font-family: arial, verdana, helvetica,sans-serif;">

Output

对于每一个询问操作,输出一行,包括1个数,表示其对应的答案

Sample Input

   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

HINT

对于30%的数据,1<=n, m<=1000
对于100%的数据,1<=n, m<=100000

题目分析:hotel那题的加强版。在这里我们不仅要维护最长连续的1,还要维护0,因为它随时可能取反。还有一点要比较注意的是各种懒惰标记之间的覆盖与合并问题,比如一个点原先有op=0的懒惰标记(将子树全部变为0),再在它上面下传一个op=2的懒惰标记(将子树全部取反),结果等于一个op=1的懒惰标记(全部变为1),之类的……

CODE:

#include
#include
#include
#include
#include
#include
#include
using namespace std;

const int maxn=100100;

struct data
{
	int llen,rlen,slen;
} ;

struct Tnode
{
	int add;
	data num[2];
	int sum;
} tree[maxn<<2];

int a[maxn];
int n,m;

void Clear(int root,int L,int R,int x)
{
	Tnode *P=tree+root;
	P->sum=(R-L+1)*x;
		
	P->num[x].llen=R-L+1;
	P->num[x].rlen=R-L+1;
	P->num[x].slen=R-L+1;
	
	P->num[!x].llen=0;
	P->num[!x].rlen=0;
	P->num[!x].slen=0;
}

void Work(int root,int L,int R,int id)
{
	int left=root<<1;
	int right=left|1;
	int mid=(L+R)>>1;
	
	int temp=tree[left].num[id].llen;
	tree[root].num[id].llen=temp;
	if ( temp==mid-L+1 ) tree[root].num[id].llen+=tree[right].num[id].llen;
	
	temp=tree[right].num[id].rlen;
	tree[root].num[id].rlen=temp;
	if ( temp==R-mid ) tree[root].num[id].rlen+=tree[left].num[id].rlen;
	
	temp=max( tree[left].num[id].slen,tree[right].num[id].slen );
	temp=max( temp,tree[left].num[id].rlen+tree[right].num[id].llen );
	tree[root].num[id].slen=temp;
}

void Up(int root,int L,int R)
{
	int left=root<<1;
	int right=left|1;
	tree[root].sum=tree[left].sum+tree[right].sum;
	
	Work(root,L,R,0);
	Work(root,L,R,1);
}

void Build(int root,int L,int R)
{
	tree[root].add=0;
	if (L==R)
	{
		Clear(root,L,R,a[L]);
		return;
	}
	
	int left=root<<1;
	int right=left|1;
	int mid=(L+R)>>1;
	
	Build(left,L,mid);
	Build(right,mid+1,R);
	
	Up(root,L,R);
}

void Change(int root,int L,int R,int op)
{
	if (op==1)
	{
		Clear(root,L,R,0);
		tree[root].add=op;
	}
	
	if (op==2)
	{
		Clear(root,L,R,1);
		tree[root].add=op;
	}
	
	if (op==3)
	{
		swap(tree[root].num[0],tree[root].num[1]);
		tree[root].sum=(R-L+1)-tree[root].sum;
		
		if (tree[root].add==1) tree[root].add++;
		else if (tree[root].add==2) tree[root].add--;
			 else if (tree[root].add==3) tree[root].add=0;
			 	  else tree[root].add=3;
	}
}

void Down(int root,int L,int R)
{
	if (!tree[root].add) return;
	
	int left=root<<1;
	int right=left|1;
	int mid=(L+R)>>1;
	
	Change(left,L,mid,tree[root].add);
	Change(right,mid+1,R,tree[root].add);
	
	tree[root].add=0;
}

void Update(int root,int L,int R,int x,int y,int op)
{
	if ( y>1;
	
	Update(left,L,mid,x,y,op);
	Update(right,mid+1,R,x,y,op);
	
	Up(root,L,R);
}

int Query(int root,int L,int R,int x,int y,int op)
{
	if ( y>1;
	
	int vl=Query(left,L,mid,x,y,op);
	int vr=Query(right,mid+1,R,x,y,op);
	
	if (op==4) return vl+vr;
	else
	{
		int temp=max(vl,vr);
		int tl=max(x,mid-tree[left].num[1].rlen+1);
		int tr=min(y,mid+tree[right].num[1].llen);
		temp=max(temp,tr-tl+1);
		return temp;
	}
}

int main()
{
	freopen("bzoj1858.in","r",stdin);
	freopen("bzoj1858.out","w",stdout);
	
	scanf("%d%d",&n,&m);
	for (int i=1; i<=n; i++) scanf("%d",&a[i]);
	
	Build(1,1,n);
	
	/*for (int i=1; i<=30; i++)
	{
		cout<add<<' ';
		cout<sum<<' ';
		cout<num[0].llen<<' ';
		cout<num[0].rlen<<' ';
		cout<num[0].slen<<' ';
		cout<num[1].llen<<' ';
		cout<num[1].rlen<<' ';
		cout<num[1].slen<<' ';
		cout<

你可能感兴趣的:(普通nlog(n)数据结构)