BZOJ 1858 SCOI2010 序列操作 线段树

题目大意:给定一个01序列,提供三种操作:

0:把一段区间的全部元素都变成0

1:把一段区间的全部元素都变成1

2:把一段区间内的全部元素全都取反

3:查询一段区间内1的个数

4:查询一段区间内最长的一段连续的1

首先假设没有操作4这就是bitset的水题。。。

多了这个,我们考虑线段树

线段树的每个节点存改动标记和翻转标记,以及该区间的信息

尽管查询的信息都是1 可是我们要连0一起保存 由于翻转后0就变成了1 1就变成了0

区间信息包含:

左端点的元素

右端点的元素

左端点開始的最长的连续元素长度

右端点開始的最长的连续元素长度

最长的连续的1的长度

最长的连续的0的长度

更新时细致讨论就可以

注意当一个点被附加上改动标记的时候 要把翻转标记清零 下传时要先下传改动标记

这题是我写过的最长的线段树。

。。

树链剖分除外

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100100
#define root 0,1,n
#define ls tree[p].lson
#define rs tree[p].rson
using namespace std;
struct seq{
	int cnt;
	bool lflag,rflag;
	int lmax,rmax;
	int max[2];
	seq(bool flag,int len)
	{
		cnt=flag?len:0;
		lflag=rflag=flag;
		lmax=rmax=len;
		max[!flag]=0;
		max[ flag]=len;
	}
	void flip(int x,int y)
	{
		lflag^=1;
		rflag^=1;
		swap(max[0],max[1]);
		cnt=(y-x+1)-cnt;
	}
};
seq un(seq*s1,seq*s2,int x,int y)
{
	seq re(0,0);
	int mid=x+y>>1;
	re.cnt=s1->cnt+s2->cnt;
	re.lflag=s1->lflag;
	re.rflag=s2->rflag;

	if( s1->lmax==(mid-x+1) && s1->lflag==s2->lflag )
		re.lmax=(mid-x+1)+s2->lmax;
	else
		re.lmax=s1->lmax;

	if( s2->rmax==(y-mid) && s2->rflag==s1->rflag )
		re.rmax=(y-mid)+s1->rmax;
	else
		re.rmax=s2->rmax;

	re.max[0]=max(s1->max[0],s2->max[0]);
	re.max[1]=max(s1->max[1],s2->max[1]);
	if(s1->rflag==s2->lflag)
		re.max[s1->rflag]=max(re.max[s2->lflag],s1->rmax+s2->lmax);
	return re;
}
struct abcd{

	int lson,rson;
	int change_mark;
	bool flip_mark;
	seq *s;
}tree[M<<1];
int n,m,tot;
void Build_Tree(int p,int x,int y)
{
	int mid=x+y>>1;
	tree[p].change_mark=-1;
	if(x==y)
	{
		scanf("%d",&mid);
		tree[p].s=new seq(mid,1);
		return ;
	}
	ls=++tot;rs=++tot;
	Build_Tree(ls,x,mid);
	Build_Tree(rs,mid+1,y);
	tree[p].s=new seq(0,0);
	*tree[p].s=un(tree[ls].s,tree[rs].s,x,y);
}
void push_down(int p,int x,int y)
{
	int mid=x+y>>1;
	if(~tree[p].change_mark)
	{
		tree[ls].change_mark=tree[p].change_mark;
		tree[rs].change_mark=tree[p].change_mark;
		tree[ls].flip_mark=0;
		tree[rs].flip_mark=0;
		*tree[ls].s=seq(tree[p].change_mark,mid-x+1);
		*tree[rs].s=seq(tree[p].change_mark,y-mid);
		tree[p].change_mark=-1;
	}
	if(tree[p].flip_mark)
	{
		tree[p].flip_mark=0;
		tree[ls].flip_mark^=1;
		tree[rs].flip_mark^=1;
		tree[ls].s->flip(x,mid);
		tree[rs].s->flip(mid+1,y);
	}
}
void change(int p,int x,int y,int l,int r,int v)
{
	int mid=x+y>>1;
	if(x==l&&y==r)
	{
		tree[p].change_mark=v;
		tree[p].flip_mark=0;
		*tree[p].s=seq(v,y-x+1);
		return ;
	}
	push_down(p,x,y);
	if(r<=mid) change(ls,x,mid,l,r,v);
	else if(l>mid) change(rs,mid+1,y,l,r,v);
	else change(ls,x,mid,l,mid,v),change(rs,mid+1,y,mid+1,r,v);
	*tree[p].s=un(tree[ls].s,tree[rs].s,x,y);
}
void flip(int p,int x,int y,int l,int r)
{
	int mid=x+y>>1;
	if(x==l&&y==r)
	{
		tree[p].flip_mark^=1;
		tree[p].s->flip(x,y);
		return ;
	}
	push_down(p,x,y);
	if(r<=mid) flip(ls,x,mid,l,r);
	else if(l>mid) flip(rs,mid+1,y,l,r);
	else flip(ls,x,mid,l,mid),flip(rs,mid+1,y,mid+1,r);
	*tree[p].s=un(tree[ls].s,tree[rs].s,x,y);
}
int count(int p,int x,int y,int l,int r)
{
	int mid=x+y>>1;
	if(x==l&&y==r)
		return tree[p].s->cnt;
	push_down(p,x,y);
	if(r<=mid) return count(ls,x,mid,l,r);
	if(l>mid) return count(rs,mid+1,y,l,r);
	return count(ls,x,mid,l,mid)+count(rs,mid+1,y,mid+1,r);
}
seq sequence(int p,int x,int y,int l,int r)
{
	int mid=x+y>>1;
	if(x==l&&y==r)
		return *tree[p].s;
	push_down(p,x,y);
	if(r<=mid) return sequence(ls,x,mid,l,r);
	if(l>mid) return sequence(rs,mid+1,y,l,r);
	seq s1=sequence(ls,x,mid,l,mid);
	seq s2=sequence(rs,mid+1,y,mid+1,r);
	return un(&s1,&s2,x,y);
}
int main()
{
	int i,p,x,y;
	cin>>n>>m;
	Build_Tree(root);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&p,&x,&y);
		x++;y++;
		switch(p)
		{
			case 0:
			case 1:change(root,x,y,p);break;
			case 2:flip(root,x,y);break;
			case 3:printf("%d\n", count(root,x,y) );break;
			case 4:printf("%d\n", sequence(root,x,y).max[1] );break;
		}
	}
	return 0;
}
时间: 2024-08-24 22:10:52

BZOJ 1858 SCOI2010 序列操作 线段树的相关文章

bzoj 1858: [Scoi2010] 序列操作 题解

[原题] 1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 1031  Solved: 529 [Submit][Status] Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区间内

bzoj 1858: [Scoi2010]序列操作

1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MB 线段树,对于每个区间需要分别维护左右中的1和0连续个数,并在op=4时特殊处理一下. Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区间内的所有数全

bzoj 1858: [Scoi2010]序列操作 || 洛谷 P2572

记一下:线段树占空间是$2^{ceil(log2(n))+1}$ 这个就是一个线段树区间操作题,各种标记的设置.转移都很明确,只要熟悉这类题应该说是没有什么难度的. 由于对某区间set之后该区间原先待进行的取反操作失效(被覆盖),因此规定tag同时存在时set的标记先进行操作,这样对区间加上set标记时要去掉原有的取反标记. 对于操作4,线段树上每个区间维护6个值,分别表示:该区间最多的连续0/1,从左侧数起/从右侧数起/其中任意位置.题目中不问连续0,为什么连续0也要维护呢?这是为了在进行取反

bzoj1858 [Scoi2010]序列操作——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1858 线段树...调了一个上午...(后面带 // 的都是改出来的) lazy 标记的下放好麻烦,还得考虑赋值和取反的先后顺序什么的... 因为在取反时把赋值标记 swap 了,所以下放的时候先判断取反再判断赋值... 而且WA了一上午的原因竟然是一开始不慎把取反以为成翻转了,后来没改干净...那个 rev 的名字啊... 总之没有太改变自己最初的想法.改了些细节就A了还是很高兴的! 代码

bzoj1858: [Scoi2010]序列操作 线段树

线段树,维护七个值两个标记. 用结构体快了一倍…… #include<bits/stdc++.h> #define N (1<<18) #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k #define L l,M,P #define R M+1,r,S #define Z int l=0,int r=n-1,int k=1 using namespace

【BZOJ-1858】序列操作 线段树

1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1961  Solved: 991[Submit][Status][Discuss] Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区

1858: [Scoi2010]序列操作

1858: [Scoi2010]序列操作 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 对于每一

bzoj 1858 序列操作(线段树)

题外话 本来想练练线段树的,然后发现这题及其蛋疼,要打一坨标记,这是我写过的最长的线段树了= = 然后我很SB的把R打成了r调了一个下午真是蛋疼QvQ Description: 给定一个0/1序列,有如下5个操作: 0:区间赋值为0 1:区间赋值为1 2:区间取反 3:询问区间内1的个数 4:询问区间内最大连续1的个数 Solution 没有操作4这显然就是个SB题,有了操作4我们需要打几个标记 我们需要从左端点开始连续0/1的个数,从右端点开始的连续0/1个数 区间内0/1个数,区间内最大连续

bzoj 2962 序列操作(线段树)

题外话 做这道题我整个人都非常的绝望,推了一会发现是线段树裸题,然后调了N久一直是WA 情况是这样的 开始WA的几百毫秒的都是由于我比较SB造成的,可是跑了10几秒的程序我查了N久也查不出错 最后灵机一动把50000改成60000就过了,也不知道为啥T_T Description 一个长度为n的序列,有3种操作 1:区间加c 2:区间取为相反数 3:询问区间中选择c个数相乘的所有方案的和mod19940417的值 Solution 这个操作3非常鬼畜,似乎没啥好的办法,但是仔细推导一番会发现这个