BZOJ_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 对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?

Input

输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目 第二行包括n个数,表示序列的初始状态 接下来m行,每行3个数,op, a, b,(0 < = op < = 4,0 < = a < = b)

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


分析:线段树处理多个标记。

我这里是先下传取反标记,后下传覆盖标记。

这样的话。如果操作是先覆盖后取反,就在取反的函数中判断一下,如果有覆盖就把覆盖的值取反。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 100050
#define ls p<<1
#define rs p<<1|1
int t[N<<2],cov[N<<2],rev[N<<2],n,m;
int lx[N<<2][2],rx[N<<2][2],nx[N<<2][2],siz[N<<2];
void pushup(int p) {
t[p]=t[ls]+t[rs];
for(int i=0;i<2;i++) {
if(nx[ls][i]==siz[ls]) lx[p][i]=siz[ls]+lx[rs][i];
else lx[p][i]=lx[ls][i];
if(nx[rs][i]==siz[rs]) rx[p][i]=siz[rs]+rx[ls][i];
else rx[p][i]=rx[rs][i];
nx[p][i]=max(max(rx[ls][i]+lx[rs][i],nx[ls][i]),nx[rs][i]);
}
}
void build(int l,int r,int p) {
cov[p]=-1;
siz[p]=r-l+1;
if(l==r) {
scanf("%d",&t[p]);
lx[p][1]=rx[p][1]=nx[p][1]=t[p];
lx[p][0]=rx[p][0]=nx[p][0]=!t[p];
return ;
}
int mid=l+r>>1;
build(l,mid,ls);
build(mid+1,r,rs);
pushup(p);
}
void pushdown(int p) {
if(rev[p]) {
t[ls]=siz[ls]-t[ls];
swap(lx[ls][0],lx[ls][1]);
swap(rx[ls][0],rx[ls][1]);
swap(nx[ls][0],nx[ls][1]);
if(cov[ls]!=-1)cov[ls]^=1;
rev[ls]^=1;
t[rs]=siz[rs]-t[rs];
swap(lx[rs][0],lx[rs][1]);
swap(rx[rs][0],rx[rs][1]);
swap(nx[rs][0],nx[rs][1]);
if(cov[rs]!=-1)cov[rs]^=1;
rev[rs]^=1;
rev[p]=0;
}
if(cov[p]!=-1) {
int d=cov[p];
cov[ls]=cov[rs]=d;
lx[ls][d]=rx[ls][d]=nx[ls][d]=siz[ls];
lx[rs][d]=rx[rs][d]=nx[rs][d]=siz[rs];
lx[ls][!d]=rx[ls][!d]=nx[ls][!d]=lx[rs][!d]=rx[rs][!d]=nx[rs][!d]=0;
t[ls]=siz[ls]*d;
t[rs]=siz[rs]*d;
cov[p]=-1;
}
}
void uprever(int l,int r,int x,int y,int p) {
if(x<=l&&y>=r) {
t[p]=siz[p]-t[p];
swap(lx[p][0],lx[p][1]);
swap(rx[p][0],rx[p][1]);
swap(nx[p][0],nx[p][1]);
rev[p]^=1;
if(cov[p]!=-1) cov[p]^=1;
return ;
}
pushdown(p);
int mid=l+r>>1;
if(x<=mid) uprever(l,mid,x,y,ls);
if(y>mid) uprever(mid+1,r,x,y,rs);
pushup(p);
}
void update(int l,int r,int x,int y,int c,int p) {
if(x<=l&&y>=r) {
cov[p]=c;
lx[p][c]=rx[p][c]=nx[p][c]=siz[p];
lx[p][!c]=rx[p][!c]=nx[p][!c]=0;
t[p]=siz[p]*c;
return ;
}
pushdown(p);
int mid=l+r>>1;
if(x<=mid) update(l,mid,x,y,c,ls);
if(y>mid) update(mid+1,r,x,y,c,rs);
pushup(p);
}
int qsum(int l,int r,int x,int y,int p) {
if(x<=l&&y>=r) return t[p];
pushdown(p);
int re=0,mid=l+r>>1;
if(x<=mid) re+=qsum(l,mid,x,y,ls);
if(y>mid) re+=qsum(mid+1,r,x,y,rs);
return re;
}
int qcon(int l,int r,int x,int y,int p) {
if(x<=l&&y>=r) return nx[p][1];
pushdown(p);
int ansl,ansr,ansm,mid=l+r>>1;
if(y<=mid) return qcon(l,mid,x,y,ls);
else if(x>mid) return qcon(mid+1,r,x,y,rs);
else {
ansl=qcon(l,mid,x,y,ls);
ansr=qcon(mid+1,r,x,y,rs);
ansm=min(mid-x+1,rx[ls][1])+min(y-mid,lx[rs][1]);
return max(max(ansl,ansr),ansm);
}
}
int main() {
scanf("%d%d",&n,&m);
int i,opt,x,y;
build(1,n,1);
for(i=1;i<=m;i++) {
scanf("%d%d%d",&opt,&x,&y);
x++;y++;
if(opt==0) {
update(1,n,x,y,0,1);
}else if(opt==1) {
update(1,n,x,y,1,1);
}else if(opt==2) {
uprever(1,n,x,y,1);
}else if(opt==3) {
printf("%d\n",qsum(1,n,x,y,1));
}else {
printf("%d\n",qcon(1,n,x,y,1));
}
}
}

BZOJ_1858_[Scoi2010]序列操作_线段树的更多相关文章

  1. BZOJ1858 [Scoi2010]序列操作(线段树)

    题目链接 [Scoi2010]序列操作 考验代码能力的一道好题. 思想还是很简单的(直接上线段树),但是比较难写. #include <bits/stdc++.h> using names ...

  2. [SCOI2010]序列操作 BZOJ1858 线段树

    题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b ...

  3. [bzoj2962]序列操作_线段树_区间卷积

    序列操作 bzoj-2962 题目大意:给定一个n个数的正整数序列,m次操作.支持:1.区间加:2.区间取相反数:3.区间求选c个数的乘积和. 注释:$1\le n,m\le 5\cdot 10^4$ ...

  4. BZOJ_2962_序列操作_线段树

    Description 有一个长度为n的序列,有三个操作1.I a b c表示将[a,b]这一段区间的元素集体增加c,2.R a b表示将[a,b]区间内所有元素变成相反数,3.Q a b c表示询问 ...

  5. bzoj 1858: [Scoi2010]序列操作【线段树】

    合并中间那块的时候没取max--WAWAWA 在线段树上维护一堆东西,分别是len区间长度,sm区间内1的个数,ll0区间从左开始最长连续0,ml0区间中间最长连续0,rl0区间从右开始最长连续0,l ...

  6. 【BZOJ1858】序列操作(线段树)

    [BZOJ1858]序列操作(线段树) 题面 BZOJ 题解 这题思路很简单,细节很烦,很码 维护区间翻转和区间赋值标记 当打到区间赋值标记时直接覆盖掉翻转标记 下放标记的时候先放赋值标记再放翻转标记 ...

  7. 【BZOJ2962】序列操作(线段树)

    [BZOJ2962]序列操作(线段树) 题面 BZOJ 题解 设\(s[i]\)表示区间内选择\(i\)个数的乘积的和 考虑如何向上合并? \(s[k]=\sum_{i=0}^klson.s[i]*r ...

  8. bzoj1858SCOI 序列操作 (线段树)

    题目大意: 给定一个长度为n的01序列为,现在有m种操作 \(0\ a\ b\) 把\([a,b]\)的数全部修改为0 \(1\ a\ b\) 把\([a,b]\)的数全部修改为1 \(2\ a\ b ...

  9. 序列操作 BZOJ2962 线段树

    分析: 数据范围表示:c特别的小(c<20) 我们可以考虑nlogn*c^2的算法. 线段树维护区间信息:f[i]表示在[l,r]这段区间中选择i个数相乘的和. 因此,我们可以将区间看成一个点, ...

随机推荐

  1. 老马Repository模式原文

    A system with a complex domain model often benefits from a layer, such as the one provided by Data M ...

  2. Ubuntu硬盘空间清理

    1.删除多余的安装下载文件: sudo aptitude autoclean sudo aptitude clean 2.删除多余的内核版本: sudo apt-get autoremove 4.去/ ...

  3. JSF-页面导航

    页面导航 1)导航处理涉及的术语: -动作值:触发动作事件的组件的action:EL方法表达式.字符串文字. -结果值:动作组件的action属性的:EL方法表达式的返回值.字符串文字:或结果组件的o ...

  4. java之jsp页面语法

    jsp页面相比静态页面html来说,就是多了一些脚本,利用这些脚本来动态地改变页面内容的显示. 1.JSP脚本写法 <% 这里写java代码; %> <%! JSP声明,用来声明变量 ...

  5. 分布式单点登录框架XXL-SSO

    <分布式单点登录框架XXL-SSO> 一.简介 1.1 概述 XXL-SSO 是一个分布式单点登录框架.只需要登录一次就可以访问所有相互信任的应用系统. 拥有"轻量级.分布式.跨 ...

  6. jquery touch 移动端上下滑动加载

    var touchStart, touchEnd, touchDiff = 80; $(window).on({ 'touchstart': function (e) { touchStart = e ...

  7. windows开发的python移植到linux的问题

    2016年1月28日 这个问题已经自己解决   file-setting下 左侧code style  line separator下拉选择unix就可以了 我在windows上开发的python想移 ...

  8. bestcoder round 74 div2

    随便看了一场以前的bestcoder,然后顺便写了一下,都不码的样子 有中文题面,这里就不写题目大意了 T1. 刚开始想复杂了,T1可能是4道题里面想的最久的 我们大概弄一下就可以发现,如果a[i]& ...

  9. 第三章之S5PV210串口初始化

    1,在start.S中执行373行b lowlevel_init跳转到/board/samsung/goni/lowlevel.S中,此代码中初始化一样硬件. 找到241行,此行执行URAT初始化,如 ...

  10. java通过反射获取字段的类型

    import java.lang.reflect.Field;  //这是需要引入的包 Field[] f = 类名.class.getDeclaredFields(); //获取该类的字段for(F ...