您的位置:首页 > 其它

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

2014-07-03 16:38 267 查看
【原题】

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]区间内的所有数全部取反,也就是说把所有的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<n)表示对于区间[a, b]执行标号为op的操作="" <="" 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

Source

Day2

【分析】真是一道猥琐的线段树的题目。以前我没写过染色等题目,但是还是YY出了譬如最大连续段数的求法。在线段树中还是要记录一下区间左端点数码、右端点数码。后来我越想越复杂,还要记录左(和右)端点如果是1(和0),最长的连续的个数。因为在L~R由L~M和M+1~R转移的时候,最长连续1的个数可能是左区间右端点连续的1和右区间左端点连续的1合并造成的。

此外,其实还是要记录最长连续0的个数,因为要区间取反的。

先挖个坑,以后有空的话再详细的写一下算法。

【代码】

#include<cstdio>
#include<algorithm>
#define N 100005
#define LL a[L].r-a[L].l+1
#define RR a[R].r-a[R].l+1
#define PP a[P].r-a[P].l+1
#define mid ((a[k].l+a[k].r)>>1)
using namespace std;
struct Tree
{
int l,r,lnum,rnum,l1,r1,l0,r0,cover,num,max0,max1;
}a[N*3];
int data
,x,y,opt,temp,n,i,Q;
inline void up(int k)
{
int L=k<<1,R=k<<1|1;
a[k].lnum=a[L].lnum;a[k].rnum=a[R].rnum;
a[k].l1=a[L].l1;if (a[L].l1==LL) a[k].l1+=a[R].l1;
a[k].r1=a[R].r1;if (a[R].r1==RR) a[k].r1+=a[L].r1;
a[k].l0=a[L].l0;if (a[L].l0==LL) a[k].l0+=a[R].l0;
a[k].r0=a[R].r0;if (a[R].r0==RR) a[k].r0+=a[L].r0;
a[k].num=a[L].num+a[R].num;
a[k].max1=max(a[L].max1,a[R].max1);
if (a[L].rnum&a[R].lnum) a[k].max1=max(a[k].max1,a[L].r1+a[R].l1);
a[k].max0=max(a[L].max0,a[R].max0);
if ((!a[L].rnum)&(!a[R].lnum)) a[k].max0=max(a[k].max0,a[L].r0+a[R].l0);
}
inline void build(int k,int l,int r)
{
a[k].l=l;a[k].r=r;
if (l==r)
{
a[k].lnum=a[k].rnum=a[k].l1=a[k].r1=a[k].num=a[k].max1=data[l];
a[k].l0=a[k].r0=a[k].max0=data[l]^1;return;
}
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
up(k);
}
inline void update(int P,int add)
{
if (add==1)
{
a[P].l1=a[P].r1=a[P].num=a[P].max1=PP;
a[P].l0=a[P].r0=a[P].max0=0;
a[P].lnum=a[P].rnum=1;
}
if (add==-1)
{
a[P].l1=a[P].r1=a[P].num=a[P].max1=a[P].lnum=a[P].rnum=0;
a[P].l0=a[P].r0=a[P].max0=PP;
}
if (add==2)
{
swap(a[P].l0,a[P].l1);swap(a[P].r0,a[P].r1);swap(a[P].max0,a[P].max1);
a[P].lnum^=1;a[P].rnum^=1;
a[P].num=PP-a[P].num;
}
}
inline void down(int k,int P)
{
update(P,temp=a[k].cover);
if (temp&1) a[P].cover=temp;
if (temp==2)
{
if (a[P].cover&1) a[P].cover=-a[P].cover;
else a[P].cover=2-a[P].cover;
}
}
void work(int k)
{
if (x<=a[k].l&&a[k].r<=y)
{
update(k,opt);
if (!a[k].cover) a[k].cover=opt;
else a[k].cover=(opt&1)?opt:((a[k].cover==2)?0:-a[k].cover);
return;
}
down(k,k<<1);down(k,k<<1|1);a[k].cover=0;
if (x<=mid) work(k<<1);
if (y>mid) work(k<<1|1);
up(k);
}
int ask(int k)
{
if (x<=a[k].l&&a[k].r<=y) return (opt==3)?a[k].num:a[k].max1;
down(k,k<<1);down(k,k<<1|1);a[k].cover=0;
int Mid=(a[k].l+a[k].r)>>1;
if (opt==3) return (((x<=Mid)?ask(k<<1):0)+((y>Mid)?ask(k<<1|1):0));
int Max=0;
if (x<=Mid) Max=max(Max,ask(k<<1));
if (y>Mid) Max=max(Max,ask(k<<1|1));
if (x<=Mid&&y>Mid) Max=max(Max,min(a[k<<1].r1,Mid-x+1)+min(a[k<<1|1].l1,y-Mid));
return Max;
}
inline int c(int k) {return (!k)?-1:k;}
inline void Read(int &x) //读入优化
int main()
{
read(n);read(Q);
for (i=1;i<=n;i++) read(data[i]);
build(1,1,n);
while (Q--)
{
read(opt);read(x);x++;read(y);y++;
if (opt<3) opt=c(opt),work(1);
else printf("%d\n",ask(1));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  题解 BZOJ 线段树