POJ 2750Potted Flower 线段树+dp 动态求环中的最大连续子序列和

本文介绍了一种解决环形序列中最大连续子序列和问题的方法,通过将环形序列转换为线性序列,并利用线段树进行区间查询优化。文章详细阐述了动态规划思想在状态转移方程中的应用,以及如何高效地更新和维护树状结构。

点击打开链接

题意:给定一个环形序列,进行在线操作,每次修改一个元素,输出环上的最大连续子列的和。并且答案不能为全部序列的和。

思路:

把环从一个地方,切断拉成一条直线,用线段树记录当前区间的非空最大子列和当前区间的非空最小子列。
如果环上的数都是正整数,答案是:环上数的总和-根结点的非空最小子列;
否则,答案是:max{根结点的非空最大子列,环上数的总和-根结点的非空最小子列},每次问答的复杂度是O(logN)。
对于一个结点,我们要存储以下信息:
int maxSeg[MAXN<<2], minSeg[MAXN<<2]; //结点[a, b]的最大非空连续子列和最小非空连续子列
int lmax[MAXN<<2]; //结点[a, b]包含a的最大非空连续子列
int rmax[MAXN<<2]; //结点[a, b]包含b的最大非空连续子列
int lmin[MAXN<<2];  //结点[a, b]包含a的最小非空连续子列
int rmin[MAXN<<2]; //结点[a, b]包含b的最小非空连续子列
int sum[MAXN<<2]; //结点[a, b]的总和
结点[a, m],[m+1, b]分别为[a, b]的左右儿子
动态规划思想由结点左右儿子的信息得到父节点的信息,设父节点下标为ind
那么:状态转移方程如下:
    maxSeg[ind] = max(max(maxSeg[ind<<1], maxSeg[ind<<1|1]), rmax[ind<<1]+lmax[ind<<1|1]); //求父节点的最大非空连续子列
    minSeg[ind] = min(min(minSeg[ind<<1], minSeg[ind<<1|1]), rmin[ind<<1]+lmin[ind<<1|1]);//求父节点的最小非空连续子列
    lmax[ind] = max(lmax[ind<<1], sum[ind<<1]+lmax[ind<<1|1]);//求父节点的包含a的最大非空连续子列
    rmax[ind] = max(rmax[ind<<1|1], sum[ind<<1|1]+rmax[ind<<1]);//求父节点的包含b的最大非空连续子列
    lmin[ind] = min(lmin[ind<<1], sum[ind<<1]+lmin[ind<<1|1]); //求父节点的包含a的最小非空连续子列
    rmin[ind] = min(rmin[ind<<1|1], sum[ind<<1|1]+rmin[ind<<1]);//求父节点的包含b的最小非空连续子列

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define LL long long
#define bug puts("***********");
#define lson L,mid,ind<<1
#define rson mid+1,R,ind<<1|1
#define lind ind<<1
#define rind ind<<1|1
using namespace std;
const int N=100010;
int sum[N*4];
int dmin[N*4],dmax[N*4];
int lmax[N*4],rmax[N*4];
int rmin[N*4],lmin[N*4];
int n;
void maintain(int ind){
     sum[ind]=sum[lind]+sum[rind];
     lmax[ind]=max(lmax[lind],sum[lind]+lmax[rind]);
     rmax[ind]=max(rmax[rind],sum[rind]+rmax[lind]);
     lmin[ind]=min(lmin[lind],sum[lind]+lmin[rind]);
     rmin[ind]=min(rmin[rind],sum[rind]+rmin[lind]);
     dmax[ind]=max(lmax[rind]+rmax[lind],max(dmax[rind],dmax[lind]));
     dmin[ind]=min(lmin[rind]+rmin[lind],min(dmin[rind],dmin[lind]));
}
void build(int L,int R,int ind){
    if(L==R){
       scanf("%d",&sum[ind]);
       dmin[ind]=dmax[ind]=lmax[ind]=rmax[ind]=rmin[ind]=lmin[ind]=sum[ind];
       return ;
    }
    int mid=(R+L)/2;
    build(lson);
    build(rson);
    maintain(ind);
}
void update(int u,int d,int L,int R,int ind){
    if(L==R){
        sum[ind]=d;
        dmin[ind]=dmax[ind]=lmax[ind]=rmax[ind]=rmin[ind]=lmin[ind]=d;
        return ;
    }
    int mid=(R+L)/2;
    if(u<=mid) update(u,d,lson);
    else update(u,d,rson);
    maintain(ind);
}
int main(){
    int m,u,d;
    scanf("%d",&n);
    build(1,n,1);
    scanf("%d",&m);
    while(m--){
        scanf("%d%d",&u,&d);
        update(u,d,1,n,1);
        if(sum[1]==dmax[1]) printf("%d\n",sum[1]-dmin[1]);
        else printf("%d\n",max(sum[1]-dmin[1],dmax[1]));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值