[CodeForces 280D] k-Maximum Subsequence Sum(毒瘤线段树)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20190102/article/details/102467453


感觉这个博客要变成有生之年系列了

题目

k-Maximum Subsequence Sum

题目大意

给出一个序列 a 1 , a 2 , , a n ( 1 n 1 0 5 ) a_1,a_2,\cdots,a_n(1\leq n\leq 10^5) q ( 1 q 1 0 5 ) q(1\leq q\leq 10^5) 次询问,每次询问的类型如下:

  • 0 i val,表示执行将 a i a_i 的值改为 v a l val
  • 1 l r k,表示选出 a l , , a r a_l,\cdots,a_r k k 个不相交的子串,使得它们的 a i a_i 值之和最大。

分析

对于每个询问,执行 k k 次如下操作:求 [ l , r ] [l,r] 的最大子段和,加进ans,然后把该字段所有元素变成它的相反数。当然,这个过程中如果 [ l , r ] [l,r] 的最大子段和出现了负数,直接退出。输出答案。
玄学直观上这个做法很好理解,相当于很多次求前缀和的过程。据说可以用网络流证。


线段树要实现两个功能:求最大子段和,区间取相反数。

其实求第一个只需要维护最大值的三坨东西,但是由于第二个东西,你还要维护最小值的三坨东西,于是加上懒标记你一共要维护16个东西。

  • Sum:区间和;
  • MaxBgSMaxBgPos:最大前缀和,最大前缀和对应的右端点;
  • MinBgSMinBgPos:最小前缀和,最小前缀和对应的右端点;
  • MaxEdSMaxEdPos:最大后缀和,最大后缀和对应的左端点;
  • MinEdSMinEdPos:最小后缀和,最小后缀和对应的左端点;
  • MaxSbSMaxSbLMaxSbR:最大子段和,最大子段和对应的左右端点;
  • MinSbSMinSbLMaxinR:最小子段和,最小子段和对应的左右端点;
  • flag:区间取反的懒标记。

PushUpQuery有点恶心,PushUp你看看代码就知道了,Query要返回一个结点,不能只返回一个值,所以Query里面要调用这个变异的PushUp

代码

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;

int read(){
    int x=0;bool f=0;char c=getchar();
    while(c<'0'||c>'9') f|=c=='-',c=getchar();
    while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
    return f?-x:x;
}

#define MAXN 100000
#define INF 0x7fffffff
int N,A[MAXN+5];
struct SegmentTree{
    #define lch (i<<1)
    #define rch (i<<1|1)
    struct Node{
        int l,r;
        int Sum;
        int MaxBgS,MinBgS,MaxBgPos,MinBgPos;
        int MaxEdS,MinEdS,MaxEdPos,MinEdPos;
        int MaxSbS,MinSbS,MaxSbL,MaxSbR,MinSbL,MinSbR;
        bool flag;
        inline void Rev(){
        	//区间取相反数,有点巧妙
            Sum=-Sum,flag^=1;
            swap(MaxBgS,MinBgS),MaxBgS=-MaxBgS,MinBgS=-MinBgS,swap(MaxBgPos,MinBgPos);
            swap(MaxEdS,MinEdS),MaxEdS=-MaxEdS,MinEdS=-MinEdS,swap(MaxEdPos,MinEdPos);
            swap(MaxSbS,MinSbS),MaxSbS=-MaxSbS,MinSbS=-MinSbS,swap(MaxSbL,MinSbL),swap(MaxSbR,MinSbR);
        }
        inline void Set(int x,int pos){
        	//区间赋值,只针对单位区间
            Sum=MaxBgS=MinBgS=MaxEdS=MinEdS=MaxSbS=MinSbS=x;
            MaxBgPos=MinBgPos=MaxEdPos=MinEdPos=MaxSbL=MaxSbR=MinSbL=MinSbR=pos;
        }
    }Tree[(MAXN<<2)+5];
    inline void PushUp(Node &A,Node B,Node C){
    	//调不出来先检查这里,Min和Max极可能写错
        A.Sum=B.Sum+C.Sum;
        //最大子段和
        if(max(B.MaxSbS,C.MaxSbS)>B.MaxEdS+C.MaxBgS){
            if(B.MaxSbS>C.MaxSbS)
                A.MaxSbS=B.MaxSbS,A.MaxSbL=B.MaxSbL,A.MaxSbR=B.MaxSbR;
            else
                A.MaxSbS=C.MaxSbS,A.MaxSbL=C.MaxSbL,A.MaxSbR=C.MaxSbR;
        }
        else
            A.MaxSbS=B.MaxEdS+C.MaxBgS,A.MaxSbL=B.MaxEdPos,A.MaxSbR=C.MaxBgPos;
        //最小子段和
        if(min(B.MinSbS,C.MinSbS)<B.MinEdS+C.MinBgS){
            if(B.MinSbS<C.MinSbS)
                A.MinSbS=B.MinSbS,A.MinSbL=B.MinSbL,A.MinSbR=B.MinSbR;
            else
                A.MinSbS=C.MinSbS,A.MinSbL=C.MinSbL,A.MinSbR=C.MinSbR;
        }
        else
            A.MinSbS=B.MinEdS+C.MinBgS,A.MinSbL=B.MinEdPos,A.MinSbR=C.MinBgPos;
        //最大前缀和
        if(B.Sum+C.MaxBgS>B.MaxBgS)
            A.MaxBgS=B.Sum+C.MaxBgS,A.MaxBgPos=C.MaxBgPos;
        else
            A.MaxBgS=B.MaxBgS,A.MaxBgPos=B.MaxBgPos;
        //最小前缀和
        if(B.Sum+C.MinBgS<B.MinBgS)
            A.MinBgS=B.Sum+C.MinBgS,A.MinBgPos=C.MinBgPos;
        else
            A.MinBgS=B.MinBgS,A.MinBgPos=B.MinBgPos;
        //最大后缀和
        if(C.Sum+B.MaxEdS>C.MaxEdS)
            A.MaxEdS=C.Sum+B.MaxEdS,A.MaxEdPos=B.MaxEdPos;
        else
            A.MaxEdS=C.MaxEdS,A.MaxEdPos=C.MaxEdPos;
        //最小后缀和
        if(C.Sum+B.MinEdS<C.MinEdS)
            A.MinEdS=C.Sum+B.MinEdS,A.MinEdPos=B.MinEdPos;
        else
            A.MinEdS=C.MinEdS,A.MinEdPos=C.MinEdPos;
    }
    inline void PushDown(int i){
        if(Tree[i].flag){
            Tree[i].flag=0;
            Tree[lch].Rev(),Tree[rch].Rev();
        }
    }
    void Build(int i,int l,int r){
        Tree[i].l=l,Tree[i].r=r;
        if(l==r){
            Tree[i].Set(A[l],l);
            return;
        }
        int mid=(l+r)>>1;
        Build(lch,l,mid);
        Build(rch,mid+1,r);
        PushUp(Tree[i],Tree[lch],Tree[rch]);
    }
    Node Query(int i,int l,int r){
        if(l<=Tree[i].l&&Tree[i].r<=r)
            return Tree[i];
        PushDown(i);
        int mid=(Tree[i].l+Tree[i].r)>>1;
        if(mid+1<=l)
        //这个mid+1我调了一晚上,,,,,,,
        //因为我平常写的线段树不是这样的,,,,,,,,
        //我一般是在上面特判return
        //但是这道题不行,,,,,,,,,,,,
            return Query(rch,l,r);
        if(r<=mid)
            return Query(lch,l,r);
        Node ret,L=Query(lch,l,r),R=Query(rch,l,r);
        PushUp(ret,L,R);
        return ret;
    }
    void Update(int i,int l,int r){
        if(r<Tree[i].l||Tree[i].r<l)
            return;
        if(l<=Tree[i].l&&Tree[i].r<=r){
            Tree[i].Rev();
            return;
        }
        PushDown(i);
        Update(lch,l,r);
        Update(rch,l,r);
        PushUp(Tree[i],Tree[lch],Tree[rch]);
    }
    void Modify(int i,int x,int pos){
        if(pos<Tree[i].l||Tree[i].r<pos)
            return;
        if(Tree[i].l==Tree[i].r){
            Tree[i].Set(Tree[i].flag?-x:x,pos);
            return;
        }
        PushDown(i);
        Modify(lch,x,pos);
        Modify(rch,x,pos);
        PushUp(Tree[i],Tree[lch],Tree[rch]);
    }
}T;

#define MAXK 20
pair<int,int> R[MAXK+5];

int main(){
    int N=read();
    for(int i=1;i<=N;i++)
        A[i]=read();
    T.Build(1,1,N);
    int Q=read();
    while(Q--){
        int opt=read();
        if(opt==0){
            int pos=read(),val=read();
            T.Modify(1,val,pos);
        }
        else{
            int Left=read(),Right=read(),K=read(),Ans=0,cnt=0;
            while(K--){
                SegmentTree::Node tmp=T.Query(1,Left,Right);
                if(tmp.MaxSbS<=0)
                    break;
                Ans+=tmp.MaxSbS;
                R[++cnt]=make_pair(tmp.MaxSbL,tmp.MaxSbR);
                T.Update(1,tmp.MaxSbL,tmp.MaxSbR);
            }
            for(int i=1;i<=cnt;i++)
                T.Update(1,R[i].first,R[i].second);
                //复原
            printf("%d\n",Ans);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/C20190102/article/details/102467453