其他分享
首页 > 其他分享> > 第k大子序列和题解

第k大子序列和题解

作者:互联网

第k大子序列和

调了一天,居然是有个字母打错了
这道题是我们集训的题,网上也没找到那个oj上有,我也没数据[1]

题面

第k大子序列和

(kmaximumsubsequencesum.cpp 1s/512MB)
题目描述

长度为nn 的数列,支持两种操作:
1.修改某个位置的值
2.询问区间[l,r][l,r] 里选出至多kk 个不相交的子段和的最大值。 一共有mm 个操作
输入格式

输入第一行一个整数n。
第二行n个整数表示该数列。
第三行一个整数m表示有m个操作。
接下来m行,每行第一个数op,若op为0表示第一个操作,op为1则表示第二个操作。
当op=0时,接下来两个整数i,k表示将第i个数修改成k。
当op=1时,接下来三个整数l,r,k,表示询问选出至多kk 个不相交的子段和的最大值。
输出格式

对于每一个询问操作输出一行一个整数。
样例
输入样例

9
9 -8 9 -1 -1 -1 9 -8 9
3
1 1 9 1
1 1 9 2
1 4 6 3

输出样例

17
25
0

样例解释
数据范围与提示

1≤n≤1051≤n≤105
|ai|≤500|ai|≤500
1≤m≤1051≤m≤105
保证任何时候数列中的值|val|≤500

说实话这道题搞懂了挺简单但是依旧十分麻烦,考虑它可以用费用流增广的思路虽然我不懂

我是按照贡献的角度考虑的,我们先讲下思路再来解释。


思路

我们使用线段树维护子段区间和最大,介于大家都会了其实我写的时候根本不会,不会的我在这里不过多赘述可以在我的这篇博客中找到[2]

每次我们取这个区间的字段和最大值,然后讲这个区间和最大值对应的区间反转

也就是将它所有的权值乘以-1,但是为了维护线段树我们需要再次更新但是你会发现无法区间进行更新只能单点修改,这个复杂度显然我们无法接受,所以我们在维护区间子段和最大值的同时还要维护一个最小值,方便区间修改。然后如果最后查询的结果大于零我们就将它加入答案中,否则便不再继续查询。必须注意的是每次查询过后必须将区间再次反转过来


解释

考虑每个数的贡献,如果所有数只被取用一次,那么最后的结果必然是不相交的子段才能得出的因为如果相交就必然会贡献两次以上所以我们每次反转就是为了保证它只被取用一次,取用后变成相反数,再次取用贡献就为零了。

代码

#include<bits/stdc++.h>
using namespace std;
const int MN=1e5+100;
int n,m;
struct tree{//还有一种写法是结构体套结构体,那是需要运算符重载,不过之后会十分方便
    int l,r,sum;
    int maxx,minn,lmax,rmax,lmin,rmin;
    int maxl,maxr,minl,minr,lmaxl,lmaxr,rmaxl,rmaxr,lminl,lminr,rminl,rminr;
    int lazy;
}t[MN<<2];
/*
类似于这样
struct rec{
	int l,r,s;//s是权值
}
struct tree{
	rec maxx,minn,lmax,lmin,rmax,rmin;
	int lazy;
}t[MN<<2];
*/
void reverse(tree &x){
    x.sum*=-1;x.lazy^=1;
    swap(x.minn,x.maxx);swap(x.minl,x.maxl);swap(x.maxr,x.minr);
    x.minn*=-1;x.maxx*=-1;
    swap(x.lmax,x.lmin);swap(x.lmaxl,x.lminl);swap(x.lmaxr,x.lminr);
    x.lmax*=-1;x.lmin*=-1;
    swap(x.rmax,x.rmin);swap(x.rmaxl,x.rminl);swap(x.rmaxr,x.rminr);
    x.rmax*=-1;x.rmin*=-1;
}

tree pushup(tree a,tree b){//这个地方极为毒瘤,其实不用记录那么多
    tree c;//我就是在这个地方改了一天,最后发现有个c打成a了
    c.l=a.l;c.r=b.r;c.sum=a.sum+b.sum;c.lazy=0;
    if(a.sum+b.lmax>a.lmax){c.lmax=a.sum+b.lmax;c.lmaxl=a.l;c.lmaxr=b.lmaxr;}
    else{c.lmax=a.lmax;c.lmaxl=a.lmaxl;c.lmaxr=a.lmaxr;}
    if(b.sum+a.rmax>b.rmax){c.rmax=b.sum+a.rmax;c.rmaxl=a.rmaxl;c.rmaxr=b.r;}
    else{c.rmax=b.rmax;c.rmaxl=b.rmaxl;c.rmaxr=b.rmaxr;}
    if(a.sum+b.lmin<a.lmin){c.lmin=a.sum+b.lmin;c.lminl=a.l;c.lminr=b.lminr;}
    else{c.lmin=a.lmin;c.lminl=a.lminl;c.lminr=a.lminr;}
    if(b.sum+a.rmin<b.rmin){c.rmin=b.sum+a.rmin;c.rminl=a.rminl;c.rminr=b.r;}
    else{c.rmin=b.rmin;c.rminl=b.rminl;c.rminr=b.rminr;}
	c.maxx=a.rmax+b.lmax;c.maxl=a.rmaxl;c.maxr=b.lmaxr;
    if(a.maxx>c.maxx){c.maxx=a.maxx;c.maxl=a.maxl;c.maxr=a.maxr;}
    if(b.maxx>c.maxx){c.maxx=b.maxx;c.maxl=b.maxl;c.maxr=b.maxr;}
	c.minn=a.rmin+b.lmin;c.minl=a.rminl;c.minr=b.lminr;
    if(a.minn<c.minn){c.minn=a.minn;c.minl=a.minl;c.minr=a.minr;}
    if(b.minn<c.minn){c.minn=b.minn;c.minl=b.minl; c.minr=b.minr;}
    return c;
}

void pushdown(int id){
    if(t[id].lazy==0)return;
    reverse(t[id<<1]),reverse(t[id<<1|1]);
    t[id].lazy=0;
}

void build(int id,int l,int r){
    t[id].l=l,t[id].r=r;
    if(l==r){
        int x;
        scanf("%d",&x);
        t[id].sum=t[id].maxx=t[id].minn=t[id].lmax=t[id].rmax=t[id].lmin=t[id].rmin=x;
        t[id].maxl=t[id].maxr=t[id].minl=t[id].minr=t[id].lmaxl=t[id].lmaxr=t[id].rmaxl=t[id].rmaxr=t[id].lminl=t[id].lminr=t[id].rminl=t[id].rminr=l;
        t[id].lazy=0;
        return;
    }
    int mid=(l+r)>>1;
    build(id<<1,l,mid),build(id<<1|1,mid+1,r);
    t[id]=pushup(t[id<<1],t[id<<1|1]);
}
void update(int id,int pos,int k){
    if(t[id].l==t[id].r){
        t[id].sum=t[id].maxx=t[id].minn=t[id].lmax=t[id].rmax=t[id].lmin=t[id].rmin=k;
        t[id].maxl=t[id].maxr=t[id].minl=t[id].minr=t[id].lmaxl=t[id].lmaxr=t[id].rmaxl=t[id].rmaxr=t[id].lminl=t[id].lminr=t[id].rminl=t[id].rminr=t[id].l;
        t[id].lazy=0;
        return;
    }
    int mid=(t[id].l+t[id].r)>>1;
    pushdown(id);
    if(pos<=mid)update(id<<1,pos,k);
    else update(id<<1|1,pos,k);
    t[id]=pushup(t[id<<1],t[id<<1|1]);
}
tree query(int id,int l,int r){
    if(l<=t[id].l&&r>=t[id].r){
        return t[id];
    }
    int mid=(t[id].l+t[id].r)>>1;
    pushdown(id);
    if(r<=mid)return query(id<<1,l,r);
    else if(l>=mid+1)return query(id<<1|1,l,r);
    else return pushup(query(id<<1,l,r),query(id<<1|1,l,r));
}
queue<tree>q;
void rev(int id,int l,int r){
    if(l<=t[id].l&&r>=t[id].r){
        reverse(t[id]);
        return;
    }
    int mid=(t[id].l+t[id].r)>>1;
    pushdown(id);
    if(l<=mid)rev(id<<1,l,r);
    if(r>=mid+1)rev(id<<1|1,l,r);
    t[id]=pushup(t[id<<1],t[id<<1|1]);
}
int main(){
//    freopen("kmaximumsubsequencesum.in","r",stdin);
//    freopen("kmaximumsubsequencesum.out","w",stdout);
    scanf("%d",&n);
    build(1,1,n);
    scanf("%d",&m);
    while(m--){
        int op,l,r,k,pos;
        scanf("%d",&op);  
        if(op){
            int ans=0;
            scanf("%d%d%d",&l,&r,&k);
            while(k--){
                tree d=query(1,l,r);
                if(d.maxx<=0)break;//贡献小于等于零就不用加在答案中了
                ans+=d.maxx;
                rev(1,d.maxl,d.maxr);
                q.push(d);
            }
            while(!q.empty()){//再次反转回原序列,用队列维护
                tree d=q.front();q.pop();
                rev(1,d.maxl,d.maxr);
            }
            printf("%d\n",ans);
        }
        else{
            scanf("%d%d",&pos,&k);
            update(1,pos,k);
        }
    }
    return 0;
}

  1. 懒得造 ↩︎

  2. 暂时没写,有空再写 ↩︎

标签:maxx,大子,int,题解,rmax,rmaxl,lmax,序列,id
来源: https://www.cnblogs.com/fanner-Blog/p/15086083.html