1. 程式人生 > >hdu 3379 Sequence operation(成段更新,區間合併)

hdu 3379 Sequence operation(成段更新,區間合併)

線段樹很好的題。涉及到的知識點: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裡面也說了取反的時候若遞迴到區間全為0或1的時候再取反違背了線段樹的本質。因此對於這兩個操作設定一個變數lazy,它的取值有-1,0,1,2,-1表示沒有操作,0和1表示被0或1覆蓋,2表示區間取反。因為兩次取反後相當於沒取,lazy標記在這裡起到很大作用,若要對這個區間取反,只需將其lazy置為2,下次再取反時就變為原來的,即置為-1。

詢問區間的1的個數時,只需要sum維護區間的和即可。求連續的1的長度時,需要區間合併,又因為有取反操作,同時還要記錄區間內連續的0的個數。

#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
//#define LL long long
#define LL __int64
#define eps 1e-12
#define PI acos(-1.0)
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 100010;

/*
節點共有如下資訊: 
lazy:標記區間中的操作或覆蓋情況,
sum:區間的和,
ll[2]表示從左邊數連續的0和1的個數,rr[2]表示從右邊數連續的0和1的個數,
long0、long1分別表示最長的連續的0和1的長度。
*/
struct node
{
	int l,r;
	int lazy;
	int sum;
	int ll[2],rr[2];
	int long1,long0;
}tree[maxn*4];

int a[maxn];
/*
向上更新,更新父親節點的和以及ll[],rr[],long0,long1
*/
void push_up(int v)
{
	int l = tree[v].l,r = tree[v].r;
	int ls = v*2,rs = v*2+1;
	int mid = (l+r)>>1;

	tree[v].sum = tree[ls].sum + tree[rs].sum;
	tree[v].ll[0] = tree[ls].ll[0];
	if(tree[ls].sum == 0)
		tree[v].ll[0] += tree[rs].ll[0];
	tree[v].ll[1] = tree[ls].ll[1];
	if(tree[ls].sum == mid-l+1)
		tree[v].ll[1] += tree[rs].ll[1];

	tree[v].rr[0] = tree[rs].rr[0];
	if(tree[rs].sum == 0)
		tree[v].rr[0] += tree[ls].rr[0];
	tree[v].rr[1] = tree[rs].rr[1];
	if(tree[rs].sum == r-mid)
		tree[v].rr[1] += tree[ls].rr[1];

	tree[v].long0 = max(max(tree[ls].long0,tree[rs].long0),tree[ls].rr[0]+tree[rs].ll[0]);
	tree[v].long1 = max(max(tree[ls].long1,tree[rs].long1),tree[ls].rr[1]+tree[rs].ll[1]);
}
/*
向下更新,lazy為0或1時直接覆蓋子區間,為2時看子區間是否被完全覆蓋,若是就直接取反,否則子節點的lazy取反
*/
void push_down(int v)
{
	if(tree[v].l == tree[v].r || tree[v].lazy == -1)
		return;
	int l = tree[v].l,r = tree[v].r;
	int ls = v*2,rs = v*2+1;
	int mid = (l + r)>>1;
	if(tree[v].lazy == 0)
	{
		tree[ls].lazy = tree[rs].lazy = 0;
		tree[ls].sum = tree[rs].sum = 0;
		tree[ls].long1 = tree[rs].long1 = 0;
		tree[ls].ll[1] = tree[ls].rr[1] = tree[rs].ll[1] = tree[rs].rr[1] = 0;
		tree[ls].ll[0] = tree[ls].rr[0] = tree[ls].long0 = mid-l+1;
		tree[rs].ll[0] = tree[rs].rr[0] = tree[rs].long0 = r-mid;
	}
	else if(tree[v].lazy == 1)
	{
		tree[ls].lazy = tree[rs].lazy = 1;
		tree[ls].sum = mid-l+1;
		tree[rs].sum = r-mid;
		tree[ls].long0 = tree[rs].long0 = 0;
		tree[ls].ll[0] = tree[ls].rr[0] = tree[rs].ll[0] = tree[rs].rr[0] = 0;
		tree[ls].ll[1] = tree[ls].rr[1] = tree[ls].long1 = mid-l+1;
		tree[rs].ll[1] = tree[rs].rr[1] = tree[rs].long1 = r-mid;
	}
	else if(tree[v].lazy == 2)
	{
		if(tree[ls].lazy == -1 || tree[ls].lazy == 2)
		{
			tree[ls].lazy = 1-tree[ls].lazy;
			tree[ls].sum = mid-l+1-tree[ls].sum;
			swap(tree[ls].long0,tree[ls].long1);
			swap(tree[ls].ll[0],tree[ls].ll[1]);
			swap(tree[ls].rr[0],tree[ls].rr[1]);
		}
		else if(tree[ls].lazy == 1)
		{
			tree[ls].lazy = 0;
			tree[ls].sum = 0;
			tree[ls].long0 = tree[ls].ll[0] = tree[ls].rr[0] = mid-l+1;
			tree[ls].long1 = tree[ls].ll[1] = tree[ls].rr[1] = 0;
		}
		else if(tree[ls].lazy == 0)
		{
			tree[ls].lazy = 1;
			tree[ls].sum = mid-l+1;
			tree[ls].long0 = tree[ls].ll[0] = tree[ls].rr[0] = 0;
			tree[ls].long1 = tree[ls].ll[1] = tree[ls].rr[1] = mid-l+1;
		}

		if(tree[rs].lazy == -1 || tree[rs].lazy == 2)
		{
			tree[rs].lazy = 1-tree[rs].lazy;
			tree[rs].sum = r-mid-tree[rs].sum;
			swap(tree[rs].long0,tree[rs].long1);
			swap(tree[rs].ll[0],tree[rs].ll[1]);
			swap(tree[rs].rr[0],tree[rs].rr[1]);
		}
		else if(tree[rs].lazy == 1)
		{
			tree[rs].lazy = 0;
			tree[rs].sum = 0;
			tree[rs].long0 = tree[rs].ll[0] = tree[rs].rr[0] = r-mid;
			tree[rs].long1 = tree[rs].ll[1] = tree[rs].rr[1] = 0;
		}
		else if(tree[rs].lazy == 0)
		{
			tree[rs].lazy = 1;
			tree[rs].sum = r-mid;
			tree[rs].long0 = tree[rs].ll[0] = tree[rs].rr[0] = 0;
			tree[rs].long1 = tree[rs].ll[1] = tree[rs].rr[1] = r-mid;
		}

	}
	tree[v].lazy = -1;
}


void build(int v, int l, int r)
{
	tree[v].l = l;
	tree[v].r = r;
	tree[v].lazy = -1;
	tree[v].sum = 0;
	tree[v].ll[0] = tree[v].rr[0] = tree[v].long0 = 0;
	tree[v].ll[1] = tree[v].rr[1] = tree[v].long1 = 0;
	if(l == r)
	{
		tree[v].sum = a[l];
		if(a[l] == 0)
			tree[v].ll[0] = tree[v].rr[0] = tree[v].long0 = 1;
		else
			tree[v].ll[1] = tree[v].rr[1] = tree[v].long1 = 1;
		return;
	}
	int mid = (l+r)>>1;
	build(v*2,l,mid);
	build(v*2+1,mid+1,r);
	push_up(v);
}

void update(int v, int l, int r, int op)
{
	if(tree[v].l == l && tree[v].r == r)
	{
		if(op == 0)
		{
			tree[v].lazy = tree[v].sum = 0;
			tree[v].ll[0] = tree[v].rr[0] = tree[v].long0 = tree[v].r - tree[v].l + 1;
			tree[v].ll[1] = tree[v].rr[1] = tree[v].long1 = 0;
		}
		else if(op == 1)
		{
			tree[v].lazy = 1;
			tree[v].sum = tree[v].r - tree[v].l + 1;
			tree[v].ll[1] = tree[v].rr[1] = tree[v].long1 = tree[v].r - tree[v].l + 1;
			tree[v].ll[0] = tree[v].rr[0] = tree[v].long0 = 0;
		}
		else if(op == 2)
		{
			if(tree[v].lazy == 0)
			{
				tree[v].lazy = 1;
				tree[v].sum = tree[v].r - tree[v].l + 1;
				tree[v].ll[1] = tree[v].rr[1] = tree[v].long1 = tree[v].r-tree[v].l+1;
				tree[v].ll[0] = tree[v].rr[0] = tree[v].long0 = 0;
			}
			else if(tree[v].lazy == 1)
			{
				tree[v].lazy = tree[v].sum = 0;
				tree[v].ll[1] = tree[v].rr[1] = tree[v].long1 = 0;
				tree[v].ll[0] = tree[v].rr[0] = tree[v].long0 = tree[v].r-tree[v].l+1;
			}
			else if(tree[v].lazy == -1 || tree[v].lazy == 2)
			{
				tree[v].lazy = 1-tree[v].lazy;
				tree[v].sum = tree[v].r-tree[v].l+1-tree[v].sum;
				swap(tree[v].long0,tree[v].long1);
				swap(tree[v].ll[0],tree[v].ll[1]);
				swap(tree[v].rr[0],tree[v].rr[1]);
			}
		}
		return;
	}
	push_down(v);
	int mid = (tree[v].l + tree[v].r) >> 1;
	if(r <= mid)
		update(v*2,l,r,op);
	else if(l > mid)
		update(v*2+1,l,r,op);
	else
	{
		update(v*2,l,mid,op);
		update(v*2+1,mid+1,r,op);
	}
	push_up(v);
}

int query(int v, int l, int r,int op)
{
	if(tree[v].l == l && tree[v].r == r)
	{
		if(op == 3)
			return tree[v].sum;
		else
			return tree[v].long1;
	}
	push_down(v);
	int mid = (tree[v].l + tree[v].r) >> 1;
	if(r <= mid)
		return query(v*2,l,r,op);
	else if(l > mid)
		return query(v*2+1,l,r,op);
	else
	{
		if(op == 3)
			return query(v*2,l,mid,op) + query(v*2+1,mid+1,r,op);
		else
		{
			//求區間[l,r]中最長的連續是1的長度。
			int tmp = min(tree[v*2].rr[1],mid-l+1) + min(tree[v*2+1].ll[1],r-mid);
			return max(tmp,max(query(v*2,l,mid,op),query(v*2+1,mid+1,r,op)));
		}
	}
}

int main()
{
	int test;
	int n,m;
	int op,l,r;

	scanf("%d",&test);
	while(test--)
	{
		scanf("%d %d",&n,&m);
		for(int i = 1; i <= n; i++)
			scanf("%d",&a[i]);
		build(1,1,n);
		while(m--)
		{
			scanf("%d %d %d",&op,&l,&r);
			l++;
			r++;
			if(op <= 2)
			{
				update(1,l,r,op);
			}
			else
			{
				int ans = query(1,l,r,op);
				printf("%d\n",ans);
			}
		}
	}
	return 0;
}