BZOJ[3638] Cf172 k-Maximum Subsequence Sum 线段树模拟费用流

21 篇文章 0 订阅
6 篇文章 0 订阅

传送门ber~

因为k很小,所以很容易想到网络流….
然后想了想复杂度不对我就弃掉了.....
其实可以拿线段树模拟一下网络流的过程
在区间中贪心选出最大连续子段和,再将他们的值变成相反数,重复k次就可以了
维护的东西太多了,写起来好恶心…..

代码如下:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define N 1000020
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
struct Data{
    int sum;
    int lmax,rmax,maxx,lm,rm,p1,p2;
    int lmin,rmin,minn,ln,rn,p3,p4;
    inline void Rever(){
        swap(lmax,lmin);swap(rmax,rmin);
        swap(maxx,minn);
        swap(lm,ln);swap(rm,rn);
        swap(p1,p3);swap(p2,p4);
        lmax*=-1;rmax*=-1;lmin*=-1;rmin*=-1;
        maxx*=-1;minn*=-1;sum*=-1;
    }
}tmp,t;
int a[N];
int n,m,k,l,r,opt,top,ans;
inline Data Merge(Data a,Data b){
    tmp.sum=a.sum+b.sum;
    if(a.lmax<a.sum+b.lmax) tmp.lmax=a.sum+b.lmax,tmp.lm=b.lm;
    else tmp.lmax=a.lmax,tmp.lm=a.lm;
    if(b.rmax<b.sum+a.rmax) tmp.rmax=b.sum+a.rmax,tmp.rm=a.rm;
    else tmp.rmax=b.rmax,tmp.rm=b.rm;
    if(a.lmin>a.sum+b.lmin) tmp.lmin=a.sum+b.lmin,tmp.ln=b.ln;
    else tmp.lmin=a.lmin,tmp.ln=a.ln;
    if(b.rmin>b.sum+a.rmin) tmp.rmin=b.sum+a.rmin,tmp.rn=a.rn;
    else tmp.rmin=b.rmin,tmp.rn=b.rn;
    if(a.rmax+b.lmax>a.maxx) tmp.maxx=a.rmax+b.lmax,tmp.p1=a.rm,tmp.p2=b.lm;
    else tmp.maxx=a.maxx,tmp.p1=a.p1,tmp.p2=a.p2;
    if(b.maxx>tmp.maxx) tmp.maxx=b.maxx,tmp.p1=b.p1,tmp.p2=b.p2;
    if(a.rmin+b.lmin<a.minn) tmp.minn=a.rmin+b.lmin,tmp.p3=a.rn,tmp.p4=b.ln;
    else tmp.minn=a.minn,tmp.p3=a.p3,tmp.p4=a.p4;
    if(b.minn<tmp.minn) tmp.minn=b.minn,tmp.p3=b.p3,tmp.p4=b.p4;
    return tmp;
}
struct PP{
    int l,r;
    PP(){}
    PP(int l,int r):l(l),r(r){}
}s[N];
struct Node{
    Node *ls,*rs;
    bool v;
    Data x;
    Node():ls(NULL),rs(NULL){}
    inline void maintain(){
        x=Merge(ls->x,rs->x);
    }
    inline void pushdown(){
        if(!v) return;
        ls->v^=1;rs->v^=1;
        ls->x.Rever();rs->x.Rever();
        v=0;
    }
}*root;
void build(int L,int R,Node *&k){
    k=new Node;k->v=false;
    if(L==R){
        k->x.lmax=k->x.rmax=k->x.maxx=k->x.lmin=k->x.rmin=k->x.minn=k->x.sum=a[L];
        k->x.ln=k->x.lm=k->x.rn=k->x.rm=k->x.p1=k->x.p2=k->x.p3=k->x.p4=R;
        return;
    }
    int mid=(L+R)>>1;
    build(L,mid,k->ls);build(mid+1,R,k->rs);
    k->maintain();
}
Data Query(int L,int R,int x,int y,Node *k){
    if(L>=x && R<=y) return k->x;
    int mid=(L+R)>>1;
    k->pushdown();
    if(y<=mid) return Query(L,mid,x,y,k->ls);
    else if(x>mid) return Query(mid+1,R,x,y,k->rs);
    else return Merge(Query(L,mid,x,y,k->ls),Query(mid+1,R,x,y,k->rs));
}
void Reverse(int L,int R,int x,int y,Node *k){
    if(L>=x && R<=y){
        k->v^=1;
        k->x.Rever();
        return;
    }
    k->pushdown();
    int mid=(L+R)>>1;
    if(y<=mid) Reverse(L,mid,x,y,k->ls);
    else if(x>mid) Reverse(mid+1,R,x,y,k->rs);
    else Reverse(L,mid,x,y,k->ls),Reverse(mid+1,R,x,y,k->rs);
    k->maintain();
}
inline void solve(int l,int r,int k){
    ans=0;
    while(k--){
        t=Query(1,n,l,r,root);
        if(t.maxx<=0) break;
        ans+=t.maxx;
        Reverse(1,n,t.p1,t.p2,root);
        s[++top]=PP(t.p1,t.p2);
    }
    while(top){
        Reverse(1,n,s[top].l,s[top].r,root);
        top--;
    }
    printf("%d\n",ans);
    return;
}
void Modify(int L,int R,int x,int v,Node *k){
    if(L==R){
        k->x.lmax=k->x.rmax=k->x.maxx=k->x.lmin=k->x.rmin=k->x.minn=k->x.sum=a[L]=v;
        k->x.ln=k->x.lm=k->x.rn=k->x.rm=k->x.p1=k->x.p2=k->x.p3=k->x.p4=L;
        return;
    }
    k->pushdown();
    int mid=(L+R)>>1;
    if(x<=mid) Modify(L,mid,x,v,k->ls);
    else Modify(mid+1,R,x,v,k->rs);
    k->maintain();
}
int main(){
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,n,root);
    m=read();
    for(int i=1;i<=m;i++){
        opt=read();l=read();r=read();
        if(opt==1){
            k=read();
            solve(l,r,k);
        }
        else Modify(1,n,l,r,root);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值