An easy problem B

Time Limit: 2000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others)

Problem Description

N个数排成一列,每个数的大小为1或者0。有两种操作,第一种操作是把一段区间内的每个数异或1,第二种操作是询问区间内最长连续1的长度。

Input

第一行一个整数N(1≤N≤100000),表示N个数。第二行N个数。接下来一行一个整数M(1≤M≤100000),表示M个操作,接下来M行每行三个整数K,L,R。K=1表示把L到R这段区间的数全部异或上1,K=0表示询问L到R这段区间内最长连续1的长度。

Output

对于每个询问,输出对应的答案,每个询问占一行。

Sample Input

5

0 1 0 0 1

5

0 1 4

1 1 1

0 1 4

1 3 4

0 1 4

Sample Output

1

2

4


解题心得:

  1. 这个题考的就是一个线段树的区间合并问题外加一个lazy标记。其实这个题比较烦,要分比较多的情况,还很容易写bug。
  2. 先不说区间里面的0-1反转问题,就只是说建树和区间合并。首先要将区间合并起来(也就是向上更新的部分pushup),就要看区间合并包括几个部分。第一个,两个小的区间合并成一个大的区间,大的区间左方的最长连续1的部分就是这个大区间左子树的左方连续1部分,但是还没完,有一种特殊情况,当左子树全是1的时候大的区间的左方连续1是左子树的长度加上右子树的左方连续1的长度。大区间右方连续1的部分和左方的算法一样。还有就是大的区间的中间的连续的最大的连续1的长度,它由左子树的右方连续1的长度加上右子树左方连续1的长度。最后将三个最长的连续1区间比较一下得出最长的连续1区间。
  3. 那么我们在树中就要维护,当前节点(线段)的长度(r-l+1),l和r的位置,最长的左、右连续1的最长长度,当前最长的连续1的最长长度,以及一个lazy标记,同时,既然最后还要0-1反转,那么不但要记录1还要记录0,这样在0-1反转的时候直接将记录的0和1的情况直接交换就行了。
  4. 最后说说lazy标记的问题,当要对当前的区间进行更新的时候,要将当前区间更新,将当前区间lazy标记,并不是只标记而不进行更新。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
struct node
{
int lsum1,rsum1,lsum2,rsum2,l,r,sum1,sum2,lazy,len;//sum1代表1的情况,sum2代表0的情况
} tree[maxn<<2];
int n,m; //lazy标记下移,0-1反转
void pushdown(int root)
{
if(!tree[root].lazy)
return;
tree[root<<1].lazy ^= 1;
tree[root<<1|1].lazy ^= 1;
swap(tree[root<<1].sum1,tree[root<<1].sum2);
swap(tree[root<<1].lsum1,tree[root<<1].lsum2);
swap(tree[root<<1].rsum1,tree[root<<1].rsum2); swap(tree[root<<1|1].sum1,tree[root<<1|1].sum2);
swap(tree[root<<1|1].lsum1,tree[root<<1|1].lsum2);
swap(tree[root<<1|1].rsum1,tree[root<<1|1].rsum2); tree[root].lazy = 0;//当前的lazy标记需要取消
} //向上更新
void pushup(int root)
{
tree[root].lsum1=tree[root<<1].lsum1;
tree[root].lsum2=tree[root<<1].lsum2;
if(tree[root<<1].lsum1==tree[root<<1].len) tree[root].lsum1+=tree[root<<1|1].lsum1;//当最左边连续1的个数等于长度的时候
if(tree[root<<1].lsum2==tree[root<<1].len) tree[root].lsum2+=tree[root<<1|1].lsum2; tree[root].rsum1=tree[root<<1|1].rsum1;
tree[root].rsum2=tree[root<<1|1].rsum2;
if(tree[root<<1|1].rsum1==tree[root<<1|1].len) tree[root].rsum1+=tree[root<<1].rsum1;
if(tree[root<<1|1].rsum2==tree[root<<1|1].len) tree[root].rsum2+=tree[root<<1].rsum2; tree[root].sum1 = max(max(tree[root<<1].sum1, tree[root<<1|1].sum1), tree[root<<1].rsum1+tree[root<<1|1].lsum1);
tree[root].sum2 = max(max(tree[root<<1].sum2, tree[root<<1|1].sum2), tree[root<<1].rsum2+tree[root<<1|1].lsum2);
} void build_tree(int l,int r,int root)
{
tree[root].l = l,tree[root].r = r,tree[root].lazy = 0;
tree[root].len = r - l + 1;
if(l == r)
{
int x;
scanf("%d",&x);
if(x)
{
tree[root].lsum1 = tree[root].rsum1 = tree[root].sum1 = 1;
tree[root].lsum2 = tree[root].rsum2 = tree[root].sum2 = 0;
}
else
{
tree[root].lsum1 = tree[root].rsum1 = tree[root].sum1 = 0;
tree[root].lsum2 = tree[root].rsum2 = tree[root].sum2 = 1;
}
return ;
} int mid = (l + r) >> 1;
build_tree(l,mid,root<<1);
build_tree(mid+1,r,root<<1|1);
pushup(root);//记得向上更新
} void change(int a,int b,int l,int r,int root)
{
if(a <= l && b >= r)
{
swap(tree[root].sum1,tree[root].sum2);
swap(tree[root].lsum1,tree[root].lsum2);
swap(tree[root].rsum1,tree[root].rsum2);
tree[root].lazy ^= 1;//lazy标记
return ;
}
int mid = (l + r) / 2;
pushdown(root);//标记下移
if(b <= mid)
change(a,b,l,mid,root<<1);
else if(a > mid)
change(a,b,mid+1,r,root<<1|1);
else
{
change(a,mid,l,mid,root<<1);
change(mid+1,b,mid+1,r,root<<1|1);
}
pushup(root);
} int query(int a,int b,int l,int r,int root)
{
if(a <= l && b >= r)
return tree[root].sum1; int mid = (l + r)/2;
pushdown(root);
if(b <= mid)
return query(a,b,l,mid,root<<1);
else if(a > mid)
return query(a,b,mid+1,r,root<<1|1);
else
{
//看起来很多 就是左子树的最大值和右子树的最大值和两个子树合并起来的值取最大的一个
int m1 = query(a,mid,l,mid,root<<1);
int m2 = query(mid+1,b,mid+1,r,root<<1|1);
int m3 = max(m1,m2);
int m4 = min(tree[root<<1].rsum1,mid-a+1);
int m5 = min(b-mid,tree[root<<1|1].lsum1);
return max(m3,m4+m5);
}
pushup(root);
} int main()
{
while(scanf("%d",&n)!=EOF)
{
build_tree(1,n,1);
scanf("%d",&m);
while(m--)
{
int k,a,b;
scanf("%d%d%d",&k,&a,&b);
if(k)
change(a,b,1,n,1);
else
{
int ans = query(a,b,1,n,1);
printf("%d\n",ans);
}
}
}
return 0;
}

线段树:CDOJ1592-An easy problem B (线段树的区间合并)的更多相关文章

  1. 线段树:CDOJ1597-An easy problem C(区间更新的线段树)

    An easy problem C Time Limit: 4000/2000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Pr ...

  2. 线段树:CDOJ1591-An easy problem A (RMQ算法和最简单的线段树模板)

    An easy problem A Time Limit: 1000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Pr ...

  3. POJ 2826 An Easy Problem? 判断线段相交

    POJ 2826 An Easy Problem?! -- 思路来自kuangbin博客 下面三种情况比较特殊,特别是第三种 G++怎么交都是WA,同样的代码C++A了 #include <io ...

  4. 线段树: CDOJ1598-加帕里公园的friends(区间合并,单点更新)

    加帕里公园的friends Time Limit: 3000/1000MS (Java/Others) Memory Limit: 131072/131072KB (Java/Others) 我还有很 ...

  5. HDU 5475(2015 ICPC上海站网络赛)--- An easy problem(线段树点修改)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5475 Problem Description One day, a useless calculato ...

  6. ACM学习历程—HDU5475 An easy problem(线段树)(2015上海网赛08题)

    Problem Description One day, a useless calculator was being built by Kuros. Let's assume that number ...

  7. POJ 2826 An Easy Problem?!(线段交点+简单计算)

    Description It's raining outside. Farmer Johnson's bull Ben wants some rain to water his flowers. Be ...

  8. hdu 5475 An easy problem(暴力 || 线段树区间单点更新)

    http://acm.hdu.edu.cn/showproblem.php?pid=5475 An easy problem Time Limit: 8000/5000 MS (Java/Others ...

  9. HDU 5475 An easy problem 线段树

    An easy problem Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pi ...

随机推荐

  1. kindeditor 修改上传图片的路径的方法

    默认情况下kindeditor上传的图片在编辑器的根目录/attached/目录下.以日期建一个目录,然后保存文件.有些时候大概我们并不想这样.考虑到更新编辑器,或更换编辑器不太方便.比如我现在想把上 ...

  2. Oracle批量SQL之 BULK COLLECT 子句

    BULK COLLECT 子句会批量检索结果,即一次性将结果集绑定到一个集合变量中,并从SQL引擎发送到PL/SQL引擎.通常可以在SELECT INTO.FETCH INTO以及RETURNING ...

  3. 一般处理程序aspx

    public bool IsReusable { get { return false; } }属性,将该属性的值改为true,为什么不起作用?按照MSDN的解释,该属性的意思是: “获取一个值,该值 ...

  4. 关于 SQL Server Reporting Services 匿名登录的解决方案

    每次访问报表都需要windows验证,这样的报表给客户确实很说不过去. SSRS 可以匿名登录的设定步骤: 环境: 开发工具:SQL Server Business Intelligence Deve ...

  5. linux touch和vi建立的文件是什么文件类型的

    都是acsii类型的文本文档,但是也可以建立其他格式的,比如vi newFile.c(c是c语言动态链接库格式)

  6. vue2.0:(八)、外卖App弹窗部分知识点总结

    本篇文章是对外卖App弹窗部分知识点的总结. 知识点一:如何从接口取出不同的图片. 答: 1.header.vue: 代码: <ul v-if="seller.supports&quo ...

  7. Date-DateFormat-Calendar-Math-regex

    一.Date类(java.util) 作用:表示时间的类,精确到毫秒,以GMT 1970年1月1日0点0分0秒起算 构造方法:     Data() ---获取当前时间      Date(long ...

  8. GreenDao 3.x 注解中ToOne和ToMany的个人理解

    GreenDao是什么东西这个就不用多说了.自从GreenDao升级到3.0之后,编译的方法发生了改变.当然这个改变是有助于快速开发的. 区别在哪随便百度一下都可以知道.这里就不多说了. 这里主要说的 ...

  9. 22/tcp open|filtered ssh 80/tcp open|filtered http

    22/tcp open|filtered ssh80/tcp open|filtered http nmap不能确定该端口是打开还是过滤,这可能是扫描一个打开的端口,但没有回应.

  10. Windows系统命令行下编译连接C/C++源代码方法

    Windows系统下编译连接源代码方法:cl -GX test.c-GX: 启动同步异常处理上面的命令会产生可执行程序:test.exe在命令行中直接输入:test.exe 就可运行该程序 Tips: ...