最长子段



给定一个序列,找出最长的子段使得子段和大于 .



处理一个前缀和数组 ,假设合法区间为 ,即 ,题目就转化为求 .

  • 解法一:线段树,将数组 从小到大排序, 相同按下标从大到小排序,每次维护一个最大值下标数组,将出现过的值删除,每次取出最大值即为所求的 .
  • 解法二:单调栈,从头开始构造一个单调递减的数组,从后面往前扫,即答案中所求的 一定会在这个单调栈中.

  • 线段树
    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e6+5;
    template<typename T>int o(T x){cout<<x<<endl;return 0;}
    int n;
    pair<int,int>p[N];
    int MAX[N*4];
    int pos[N];
    int main(){
      function<void(int l,int r,int rt)>
      build=[&](int l,int r,int rt){
          if(l==r){
              pos[MAX[rt]=l]=rt;return;
          }
          int mid=l+r>>1;
          build(l,mid,rt<<1);
          build(mid+1,r,rt<<1|1);
          MAX[rt]=max(MAX[rt<<1|1],MAX[rt<<1]);
      };
      function<void(int index)>
      up=[&](int index){
          int x=pos[index];
          MAX[x]=-1;
          while(x>1)MAX[x>>1]=max(MAX[x],MAX[x^1]),x>>=1;
      };
      cin>>n;
      for(int i=1;i<=n;i++){
          int x;
          scanf("%d",&x);
          p[i]={p[i-1].first+x,-i};
      }
      p[0]={0,0};
      sort(p,p+1+n);
      build(0,n,1);
      int ans=0;
      for(int i=0;i<=n;i++){
          int x=MAX[1];
          ans=max(ans,x+p[i].second);
          up(-p[i].second);
      }
      o(ans);
    }
  • 单调栈
    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e6+10;
    int p[N]={0};
    int dp[N],Index[N];
    int t=0;
    int main(){
          int n;
          cin>>n;
          for(int i=1;i<=n;i++){
                  int x;
                  scanf("%d",&x);
                  p[i]=p[i-1]+x;
          }
          for(int i=0;i<=n;i++){
                  if(!t||dp[t]>p[i])dp[++t]=p[i],Index[t]=i;
          }
          int ans=0;
          for(int i=n;i>=1;i--){
                  while(Index[t]>=i)t--;
                  while(t>0&&p[i]-dp[t]>0)ans=max(ans,i-Index[t]),t--;
          }
          cout<<ans<<endl;
    }