bzoj3638 cf172 k-Maximum Subsequence Sum(线段树模拟费用流)

本文介绍了一种结合线段树与费用流算法解决特定问题的方法,通过手动模拟增广过程来处理区间内的最大子序列求和问题,并支持动态更新及多次查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少。

Input

The first line contains integer n (1 ≤ n ≤ 105), showing how many numbers the sequence has. The next line contains n integers a1, a2, …, an (|ai| ≤ 500).

The third line contains integer m (1 ≤ m ≤ 105) — the number of queries. The next m lines contain the queries in the format, given in the statement.

All changing queries fit into limits: 1 ≤ i ≤ n, |val| ≤ 500.

All queries to count the maximum sum of at most k non-intersecting subsegments fit into limits: 1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 20. It is guaranteed that the number of the queries to count the maximum sum of at most k non-intersecting subsegments doesn’t exceed 10000.

Output

For each query to count the maximum sum of at most k non-intersecting subsegments print the reply — the maximum sum. Print the answers to the queries in the order, in which the queries follow in the input.

Sample Input

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

Sample Output

17
25
0

HINT

In the first query of the first example you can select a single pair (1, 9). So the described sum will be 17.

Look at the second query of the first example. How to choose two subsegments? (1, 3) and (7, 9)? Definitely not, the sum we could get from (1, 3) and (7, 9) is 20, against the optimal configuration (1, 7) and (9, 9) with 25.

The answer to the third query is 0, we prefer select nothing if all of the numbers in the given interval are negative.

Source


[ Submit][ Status][ Discuss]



分析:
详尽题解

费用流的构图,线段树手动模拟增广过程

线段树维护方法:
维护一段区间的最大子序列

每次我们提取出一个最大子序列时,我们要把这个子序列取反(*-1,防止重复选择),所以还需要维护最小子序列

每进行一次取反,当前最大和子序列一定变成最小和子序列,最小和子序列一定变成最大,那么直接swap一下就可以了

鉴于一次询问需要增广K次,每一次都要要取反,所以需要开一个栈记录一下当前询问所反转的所有区间,在结束时还原

总的时间复杂度是 O(knlogn) O ( k n l o g n )

#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

const int N=100010;
struct node{
    int lx,rx,mx,sum;
    int lp,rp,p1,p2;
    void init(int l,int val) {
        lp=rp=p1=p2=l;
        lx=rx=mx=sum=val;
    }
};
struct Tree{
    int l,r;
    bool flag;
    node mn,mx;
    void init(int val) {
        mx.init(l,val);
        mn.init(l,-val);
    }
};
Tree t[N<<2];
int n,m,a[N];

node merge(node a,node b) {
    node t;
    t.sum=a.sum+b.sum;
    t.lx=a.lx; t.lp=a.lp;
    if (a.sum+b.lx>t.lx) t.lx=a.sum+b.lx,t.lp=b.lp;

    t.rx=b.rx; t.rp=b.rp;
    if (b.sum+a.rx>t.rx) t.rx=b.sum+a.rx,t.rp=a.rp;

    t.mx=a.rx+b.lx; t.p1=a.rp; t.p2=b.lp;
    if (a.mx>t.mx) t.mx=a.mx,t.p1=a.p1,t.p2=a.p2;
    if (b.mx>t.mx) t.mx=b.mx,t.p1=b.p1,t.p2=b.p2;
    return t;
}

void update(int bh) {
    t[bh].mn=merge(t[bh<<1].mn,t[bh<<1|1].mn);
    t[bh].mx=merge(t[bh<<1].mx,t[bh<<1|1].mx);
} 

void push(int bh) {
    if (t[bh].l==t[bh].r) return;
    if (t[bh].flag) {
        swap(t[bh<<1].mn,t[bh<<1].mx);
        swap(t[bh<<1|1].mn,t[bh<<1|1].mx);
        t[bh<<1].flag^=1; t[bh<<1|1].flag^=1;
        t[bh].flag^=1;
    }
}

void build(int bh,int l,int r) {
    t[bh].l=l; t[bh].r=r;
    if (l==r) {
        t[bh].init(a[l]);
        return;
    }
    int mid=(l+r)>>1;
    build(bh<<1,l,mid);
    build(bh<<1|1,mid+1,r);
    update(bh);
}

void change(int bh,int pos,int z) {
    push(bh);
    int l=t[bh].l,r=t[bh].r,mid=(l+r)>>1;
    if (l==r) {
        t[bh].init(z);
        return;
    }
    if (pos<=mid) change(bh<<1,pos,z);
    else change(bh<<1|1,pos,z);
    update(bh);
}

void reverse(int bh,int L,int R) {
    push(bh);
    int l=t[bh].l,r=t[bh].r,mid=(l+r)>>1;
    if (l>=L&&r<=R) {
        swap(t[bh].mn,t[bh].mx);
        t[bh].flag^=1;
        return;
    }
    if (L<=mid) reverse(bh<<1,L,R);
    if (R>mid) reverse(bh<<1|1,L,R);
    update(bh);
}

node ask(int bh,int L,int R) {
    push(bh);
    int l=t[bh].l,r=t[bh].r,mid=(l+r)>>1;
    if (l>=L&&r<=R) return t[bh].mx;
    if (R<=mid) return ask(bh<<1,L,R);
    else if (L>mid) return ask(bh<<1|1,L,R);
    else return merge(ask(bh<<1,L,mid),ask(bh<<1|1,mid+1,R));
}

struct point{
    int x,y;
};
point q[N];
int top;

void solve(int l,int r,int x) {
    top=0;
    int ans=0;
    while (x--) {
        node t=ask(1,l,r);
        if (t.mx>0) ans+=t.mx;
        else break;
        reverse(1,t.p1,t.p2);
        top++;
        q[top].x=t.p1; q[top].y=t.p2;
    }
    printf("%d\n",ans);
    for (int i=top;i>0;i--)
        reverse(1,q[i].x,q[i].y);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    int opt,l,r,x;
    while (m--) {
        scanf("%d",&opt);
        if (opt==1) {
            scanf("%d%d%d",&l,&r,&x);
            solve(l,r,x);
        }
        else {
            scanf("%d%d",&l,&x);
            change(1,l,x);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值