BZOJ_1858_[Scoi2010]序列操作_线段树
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
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
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]序列操作_线段树的更多相关文章
- BZOJ1858 [Scoi2010]序列操作(线段树)
题目链接 [Scoi2010]序列操作 考验代码能力的一道好题. 思想还是很简单的(直接上线段树),但是比较难写. #include <bits/stdc++.h> using names ...
- [SCOI2010]序列操作 BZOJ1858 线段树
题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b ...
- [bzoj2962]序列操作_线段树_区间卷积
序列操作 bzoj-2962 题目大意:给定一个n个数的正整数序列,m次操作.支持:1.区间加:2.区间取相反数:3.区间求选c个数的乘积和. 注释:$1\le n,m\le 5\cdot 10^4$ ...
- BZOJ_2962_序列操作_线段树
Description 有一个长度为n的序列,有三个操作1.I a b c表示将[a,b]这一段区间的元素集体增加c,2.R a b表示将[a,b]区间内所有元素变成相反数,3.Q a b c表示询问 ...
- bzoj 1858: [Scoi2010]序列操作【线段树】
合并中间那块的时候没取max--WAWAWA 在线段树上维护一堆东西,分别是len区间长度,sm区间内1的个数,ll0区间从左开始最长连续0,ml0区间中间最长连续0,rl0区间从右开始最长连续0,l ...
- 【BZOJ1858】序列操作(线段树)
[BZOJ1858]序列操作(线段树) 题面 BZOJ 题解 这题思路很简单,细节很烦,很码 维护区间翻转和区间赋值标记 当打到区间赋值标记时直接覆盖掉翻转标记 下放标记的时候先放赋值标记再放翻转标记 ...
- 【BZOJ2962】序列操作(线段树)
[BZOJ2962]序列操作(线段树) 题面 BZOJ 题解 设\(s[i]\)表示区间内选择\(i\)个数的乘积的和 考虑如何向上合并? \(s[k]=\sum_{i=0}^klson.s[i]*r ...
- bzoj1858SCOI 序列操作 (线段树)
题目大意: 给定一个长度为n的01序列为,现在有m种操作 \(0\ a\ b\) 把\([a,b]\)的数全部修改为0 \(1\ a\ b\) 把\([a,b]\)的数全部修改为1 \(2\ a\ b ...
- 序列操作 BZOJ2962 线段树
分析: 数据范围表示:c特别的小(c<20) 我们可以考虑nlogn*c^2的算法. 线段树维护区间信息:f[i]表示在[l,r]这段区间中选择i个数相乘的和. 因此,我们可以将区间看成一个点, ...
随机推荐
- java 深入理解内部类以及之间的调用关系
什么是内部类 内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用public和 ...
- JAVA调用数据库存储过程
下面将举出JAVA对ORACLE数据库存储过程的调用 ConnUtils连接工具类:用来获取连接.释放资源 复制代码 package com.ljq.test; import jav ...
- oracle面试题目总结
阿里巴巴公司DBA笔试题 http://searchdatabase.techtarget.com.cn/tips/2/2535002.shtml 注:以下题目,可根据自己情况挑选题目作答,不必 ...
- WSGI及gunicorn指北(二)
pyg0已经大概了解了wsgi.现在他决定深入探索他们实际在生产环境里用到的web 服务器 -gunicorn. 先来看看官网的介绍:Gunicorn 是一个运行在Unix上的python WSGI ...
- java学习面试精华
1.线程状态转移 (1)线程生命周期中的5种状态 新建(New).就绪(Runnable).运行(Running).阻塞(Bolocked)和死亡(Dead) 新建(New):程序使用new关键字创建 ...
- tomcat 绑定ipv4端口
在<tomcat>/bin目录打开catalina.sh,然后添加如下内容: JAVA_OPTS="$JAVA_OPTS -Djava.net.preferIPv4Stack=t ...
- JavaScript打开新窗口被拦截问题
新窗口打开页面,一个很常用的效果,至于代码,一般第一反应都是这么写: window.open(url); 但是主流的浏览器都会拦截这种效果(可能这些年弹窗广告太多,如果浏览器不拦截,用户受不了) ...
- 搭建centos7的开发环境3-Spark安装配置
说起大数据开发,必然就会提到Spark,在这片博文中,我们就介绍一下Spark的安装和配置. 这是Centos7开发环境系列的第三篇,本篇的安装会基于之前的配置进行,有需要的请回复搭建centos7的 ...
- git命令行常用几个指令(细节问题)
GIT PUSH/PULL时总需要输入用户名密码的解决方案: git config --global credential.helper store 查看当前的配置信息: git config --s ...
- jenkins中集成commander应用
jenkins中集成commander应用 jenkins 集成测试 promotion 最近参加公司的集成测试平台的开发,在开发中遇到了不少问题,两个星期的迭代也即将完成,在这也用这篇博客记录下开发 ...