ZROI2018提高day4t3

本文探讨了在一个由0和1组成的数组中,通过将0视为-1、1视为1,利用线段树来高效求解任意区间内的最大值问题。文章详细介绍了如何构建线段树,并通过三个可能的情况来更新节点,最终实现区间查询。

传送门

分析

我们假设如果一个点是0则它的值为-1,如果一个点是1则值为1,则一个区间的答案便是max(pre[i]+sur[i]),这里的pre[i]表示此区间i点和它之前的的前缀的最大值,sur[i]表示i点之后的后缀最大值。所以为了维护每个区间的答案我们可以用线段树进行维护。而对于一个由两个区间拼成的区间它的答案只有三种

1. 由左区间所选的pre加上右区间的sur

2. 由左区间的全部加上右区间的pre和sur

3. 由左区间所选的pre和sur加上右区间的全部

而这个区间的答案即为这三种的最大值。

至于为什么是这三种情况请自行画图思考。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
char s[200100];
int a[200100];
struct node {
      int pre,sur,ans,sum;
};
node d[800400];
inline void up(int wh){
      d[wh].sum=d[wh<<1].sum+d[wh<<1|1].sum;
      d[wh].pre=max(d[wh<<1].pre,d[wh<<1].sum+d[wh<<1|1].pre);
      d[wh].sur=max(d[wh<<1|1].sur,d[wh<<1|1].sum+d[wh<<1].sur);
      d[wh].ans=max(d[wh<<1].pre+d[wh<<1|1].sur,
      max(d[wh<<1].sum+d[wh<<1|1].ans,d[wh<<1].ans+d[wh<<1|1].sum));
      return;
}
inline void build(int le,int ri,int wh){
      if(le==ri){
          d[wh].ans=d[wh].pre=d[wh].sur=max(a[le],0);
          d[wh].sum=a[le];
          return;
      }
      int mid=(le+ri)>>1;
      build(le,mid,wh<<1);
      build(mid+1,ri,wh<<1|1);
      up(wh);
      return;
}
inline node q(int le,int ri,int wh,int x,int y){
      if(le>=x&&ri<=y)return d[wh];
      int mid=(le+ri)>>1;
      node ans,a,b;
      int cnt=0;
      if(mid>=x)cnt++,ans=a=q(le,mid,wh<<1,x,y);
      if(mid<y)cnt++,ans=b=q(mid+1,ri,wh<<1|1,x,y);
      if(cnt==1)return ans;
      ans.sum=a.sum+b.sum;
      ans.pre=max(a.pre,a.sum+b.pre);
      ans.sur=max(b.sur,b.sum+a.sur);
      ans.ans=max(a.pre+b.sur,max(a.sum+b.ans,a.ans+b.sum));
      return ans;
}
int main(){
      int n,m,i,j,k,x,y;
      scanf("%d%d",&n,&m);
      scanf("%s",s);
      for(i=1;i<=n;i++)
        if(s[i-1]=='0')a[i]=-1;
          else a[i]=1;
      build(1,n,1);
      for(i=1;i<=m;i++){
          int x,y;
          scanf("%d%d",&x,&y);
          printf("%d\n",q(1,n,1,x,y).ans);
      }
      return 0;
}

转载于:https://www.cnblogs.com/yzxverygood/p/9688569.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值