CodeForces - 280D k-Maximum Subsequence Sum(区间最大k段和)(线段树 + 最大子段和 + 区间修改 + 区间查询 + 单点修改)

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

题目

题意

给定n个数的序列,定义两个操作
0kval 把序列第k个数的值变为val
1lrk 询问在区间 AlAr 中,选取 m 段不相交的子区间,使得这 m 段子区间的和最大,其中0mk

思路

首先这里讲了如何解决 k=1 的情况,即用线段树维护并进行合并 blablabla....
然后考虑如何取k段,我们只需要去给定区间取 k 次区间最大子段和,累加答案后,对取出的区间做区间取反操作。而当所取的区间最大子段和小于 0 的时候,则跳出不计
做法的正确性可以从贪心或者费用流的思想上证明
之后再进行单点修改就行了

那么实现的就在维护的是maxs,maxr,maxl 这三个值的时候,如何进行取反操作,如何在找出 maxs 的同时记录下,对应的哪一段区间。(这三个变量的含义见区间最大子段和部分)
而取反实际上就是将最大子段和以及最小子段和进行了交换,而我们只需要同时维护最大子段和最小子段和,记录下对应的区间信息,在取反操作时对调就可以了。而又由于我们的 maxs 是用 maxr,maxl来维护的,所有后两者的区间信息也需要记录

于是就有了这么一个维护了18个值的线段树…
sum   区间和   [l, r] 区间的左右边界
maxs 最大子段和 [maxsl, maxsr] 最大子段和的左右边界
mins 最小子段和 [maxsl, maxsr] 最小子段和的左右边界
maxl 最大前缀和 maxlp 最大前缀和的右边界
minl  最小前缀和 minlp 最小前缀和的右边界
maxr 最大后缀和 maxrp 最大后缀和的左边界
minr  最小后缀和 minrp 最小后缀和的左边界
tag 区间取反标记

然后将区间合并更新所有的最佳情况函数化为了 merge( )
又由于使用了 merge,所以 tag 需要在 merge 前记录一下并赋值

其他的就是基本的线段树的操作了

代码

#include <bits/stdc++.h>
using namespace std;
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d%d",&n,&m)
#define sddd(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define pd(n) printf("%d\n", (n))
#define pdd(n,m) printf("%d %d", n, m)
#define pld(n) printf("%lld\n", n)
#define pldd(n,m) printf("%lld %lld\n", n, m)
#define sld(n) scanf("%lld",&n)
#define sldd(n,m) scanf("%lld%lld",&n,&m)
#define slddd(n,m,k) scanf("%lld%lld%lld",&n,&m,&k)
#define sf(n) scanf("%lf",&n)
#define sff(n,m) scanf("%lf%lf",&n,&m)
#define sfff(n,m,k) scanf("%lf%lf%lf",&n,&m,&k)
#define ss(str) scanf("%s",str)
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define mm(a,n) memset(a, n, sizeof(a))
#define debug(x) cout<<#x<<": "<<x<<endl
#define pb push_back
#define mk make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
typedef pair<int,int> PII;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const ll mod = 1000000007;
const double eps = 1e-9;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+5;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
template<typename T>inline void read(T &x){
    T f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(x=0;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    x*=f;
}
// head

struct Node {
    int l, r;
    int sum, tag;
    int maxs, maxl, maxr;
    int maxsl, maxsr, minsl, minsr;
    int maxlp, maxrp, minlp, minrp;
    int mins, minl, minr;
} tree[maxn<<2];
int num[maxn];

Node merged(Node ls, Node rs){
    Node ret;
    ret.sum = ls.sum + rs.sum;
    ret.l = ls.l;
    ret.r = rs.r;
    ret.tag = 0;
    if(ls.maxs > rs.maxs){
        ret.maxs  = ls.maxs;
        ret.maxsl = ls.maxsl;
        ret.maxsr = ls.maxsr;
    }
    else {
        ret.maxs  = rs.maxs;
        ret.maxsl = rs.maxsl;
        ret.maxsr = rs.maxsr;
    }
    if(ret.maxs < ls.maxr + rs.maxl) {
        ret.maxs  = ls.maxr + rs.maxl;
        ret.maxsl = ls.maxrp;
        ret.maxsr = rs.maxlp;
    }

    if(ls.maxl > ls.sum + rs.maxl){
        ret.maxl  = ls.maxl;
        ret.maxlp = ls.maxlp;
    }
    else{
        ret.maxl  = ls.sum + rs.maxl;
        ret.maxlp = rs.maxlp;
    }

    if(rs.maxr > rs.sum + ls.maxr){
        ret.maxr  = rs.maxr;
        ret.maxrp = rs.maxrp;
    }
    else {
        ret.maxr  = rs.sum + ls.maxr;
        ret.maxrp = ls.maxrp;
    }

    if(ls.mins < rs.mins){
        ret.mins  = ls.mins;
        ret.minsl = ls.minsl;
        ret.minsr = ls.minsr;
    }
    else {
        ret.mins  = rs.mins;
        ret.minsl = rs.minsl;
        ret.minsr = rs.minsr;
    }
    if(ret.mins > ls.minr + rs.minl) {
        ret.mins  = ls.minr + rs.minl;
        ret.minsl = ls.minrp;
        ret.minsr = rs.minlp;
    }

    if(ls.minl < ls.sum + rs.minl){
        ret.minl  = ls.minl;
        ret.minlp = ls.minlp;
    }
    else{
        ret.minl  = ls.sum + rs.minl;
        ret.minlp = rs.minlp;
    }

    if(rs.minr < rs.sum + ls.minr){
        ret.minr  = rs.minr;
        ret.minrp = rs.minrp;
    }
    else {
        ret.minr  = rs.sum + ls.minr;
        ret.minrp = ls.minrp;
    }

    return ret;
}

void inv(int i){
    tree[i].sum = -tree[i].sum;
    tree[i].tag ^= 1;
    swap(tree[i].maxs, tree[i].mins);
    swap(tree[i].maxsl, tree[i].minsl);
    swap(tree[i].maxsr, tree[i].minsr);
    tree[i].maxs = -tree[i].maxs; tree[i].mins = -tree[i].mins;

    swap(tree[i].maxl, tree[i].minl);
    swap(tree[i].maxlp, tree[i].minlp);
    tree[i].maxl = -tree[i].maxl; tree[i].minl = -tree[i].minl;

    swap(tree[i].maxr, tree[i].minr);
    swap(tree[i].maxrp, tree[i].minrp);
    tree[i].maxr = -tree[i].maxr; tree[i].minr = -tree[i].minr;
}

void push_down(int i){
    if(tree[i].tag){
        //cout<<"pd "<<i<<endl;
        inv(i<<1);
        inv(i<<1|1);
        tree[i] = merged(tree[i<<1], tree[i<<1|1]);
        tree[i].tag = 0;
    }
}

void build(int i, int l, int r) {

    tree[i].l = l;
    tree[i].r = r;
    tree[i].tag = 0;
    if(l == r) {
        tree[i].sum  = tree[i].maxs = tree[i].mins = num[l];
        tree[i].maxl = tree[i].maxr = num[l];
        tree[i].minl = tree[i].minr = num[l];
        tree[i].maxsl = tree[i].maxsr = l;
        tree[i].minsl = tree[i].minsr = l;
        tree[i].maxlp = tree[i].maxrp = l;
        tree[i].minlp = tree[i].minrp = l;
        return ;
    }

    int mid = (l+r)>>1;
    build(i<<1, l, mid);
    build(i<<1|1, mid+1, r);    //子树建树后更新特征值
    int tag = tree[i].tag;
    tree[i] = merged(tree[i<<1], tree[i<<1|1]);
    tree[i].tag = tag;
    return ;
}

Node query(int i, int l, int r) {
    if(tree[i].l==l && tree[i].r==r)
        return tree[i];
    push_down(i);
    int mid = (tree[i].l+tree[i].r)>>1;
    if(mid >= r)
        return query(i<<1, l, r);
    else if(mid < l)
        return query(i<<1|1, l, r);
    else {
        Node ls = query(i, l, mid);
        Node rs = query(i, mid+1, r);
        Node ans = merged(ls, rs);
        return ans;
    }
}

void setk(int i, int k, int val){
    if(tree[i].l == tree[i].r){
        if(tree[i].tag) val = -val;
        tree[i].sum  = tree[i].maxs = tree[i].mins = val;
        tree[i].maxl = tree[i].maxr = val;
        tree[i].minl = tree[i].minr = val;
        return ;
    }
    push_down(i);
    int mid = (tree[i].l+tree[i].r)>>1;
    if(k <= mid)
        setk(i<<1, k, val);
    else setk(i<<1|1, k, val);

    int tag = tree[i].tag;
    tree[i] = merged(tree[i<<1], tree[i<<1|1]);
    tree[i].tag = tag;
}


void settag(int i, int l, int r) {
    if(tree[i].l==l && tree[i].r==r){
        inv(i);
        return ;
    }
    push_down(i);
    int mid = (tree[i].l+tree[i].r)>>1;
    if(mid >= r)
        settag(i<<1, l, r);
    else if(mid < l)
        settag(i<<1|1, l, r);
    else {
        settag(i<<1, l, mid);
        settag(i<<1|1, mid+1, r);
    }

    tree[i] = merged(tree[i<<1], tree[i<<1|1]);
}

int lc[maxn], rc[maxn];

int main()
{
    int n, m, op, l, r, k, val;
    sd(n);
    rep(i, 1, n+1)
        sd(num[i]);

    build(1, 1, n);
    sd(m);

    rep(i, 0, m){
        sd(op);
        if(op) {
            int sum = 0, cnt = 0;
            sddd(l, r, k);
            rep(j, 0, k){
                Node now = query(1, l, r);
                if(now.maxs < 0) break;
                sum += now.maxs;
                settag(1, now.maxsl, now.maxsr);
                lc[cnt] = now.maxsl;
                rc[cnt++] = now.maxsr;
            }
            pd(sum);
            rep(j, 0, cnt)
                settag(1, lc[j], rc[j]);
        }
        else {
            sdd(k, val);
            setk(1, k, val);
        }
    }

    return 0;
}

小结

merge 函数枚举情况写的有点丑..但是一时半会儿没有想到有什么很好的优化办法
在每次操作后将线段树还原,需要把取反区间再取反一边。如果是遍历线段树重置的话会 T

展开阅读全文

没有更多推荐了,返回首页