spoj 1557. Can you answer these queries II(线段树)

本文针对 SPOJ 的 GSS2 问题,介绍了一种利用线段树解决区间最大子段和问题的方法,并特别处理了重复数字的情况。通过离线处理和使用四个值维护线段树,实现了高效的求解。

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

题目链接:http://www.spoj.com/problems/GSS2/

 

题意:还是求区间最大子段和,不过要求重复的数字只能算一次。

 

思路:感觉这题好难想……看了看题解,总算想明白了。首先是解决重复数字的问题,这需要离线处理,将每个数字从左到右依次插入到线段树中,用pre[]记录这个数字上一次出现的位置,那么在这个数字之前的就不需要增加当前的数字了,因此只要更新处理它们之间的数字就行了。

           另外,用线段树维护的值有4个,分别是:tosum[]:从某一点到当前节点的区间中区间和的最大值,maxseg[]:从某一点到另一点的区间中,区间和最大值(也就是答案)

                                                                              addv[]:懒惰标记,在该区间要加的数的和,maxup[]:也是懒惰标记,表示在该区间加的数的历史中最大的那个。

为什么要用两个懒惰标记呢?这里举个例子,比如当前区间要加的数是-4,但是它的左儿子之前有一个要加的数是4,如果直接这么下传,那么要加的数就是0了,但实际上最大的那个区间要加4才能获得最大值,因此要用另一个数组来记录历史中要增加的最大值。

 

代码:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
const int N=100000;
int maxseg[maxn<<2],tosum[maxn<<2],maxup[maxn<<2],addv[maxn<<2];
int num[maxn],ans[maxn],pre[maxn*2];
struct Node
{
    int l,pos;
};
vector<Node>querys[maxn];
void PushUp(int rt)
{
    maxseg[rt]=max(maxseg[rt<<1],maxseg[rt<<1|1]);
    tosum[rt]=max(tosum[rt<<1],tosum[rt<<1|1]);
}
void PushDown(int rt)
{
    int ls=rt<<1,rs=rt<<1|1;
    maxup[ls]=max(maxup[ls],maxup[rt]+addv[ls]);
    maxup[rs]=max(maxup[rs],maxup[rt]+addv[rs]);
    maxseg[ls]=max(maxseg[ls],tosum[ls]+maxup[rt]);
    maxseg[rs]=max(maxseg[rs],tosum[rs]+maxup[rt]);
    addv[ls]+=addv[rt];addv[rs]+=addv[rt];
    tosum[ls]+=addv[rt];tosum[rs]+=addv[rt];
    addv[rt]=maxup[rt]=0;
}
void build(int l,int r,int rt)
{
    maxseg[rt]=tosum[rt]=addv[rt]=maxup[rt]=0;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
}
void Update(int L,int R,int l,int r,int rt,int v)
{
    if(l>=L&&r<=R)
    {
        maxup[rt]=max(maxup[rt],addv[rt]+v);
        maxseg[rt]=max(maxseg[rt],tosum[rt]+v);
        addv[rt]+=v;tosum[rt]+=v;
        return;
    }
    PushDown(rt);
    int m=(l+r)>>1;
    if(m>=L)
     Update(L,R,l,m,rt<<1,v);
    if(m<R)
     Update(L,R,m+1,r,rt<<1|1,v);
    PushUp(rt);
}
int Query(int L,int R,int l,int r,int rt)
{
    if(l>=L&&r<=R) return maxseg[rt];
    PushDown(rt);
    int m=(l+r)>>1;
    int res=0;
    if(m>=L)
     res=max(res,Query(L,R,l,m,rt<<1));
    if(m<R)
     res=max(res,Query(L,R,m+1,r,rt<<1|1));
    return res;
}
void solve(int n)
{
    memset(pre,0xff,sizeof(pre));
    int l,sz;
    Node tmp;
    for(int i=1;i<=n;++i)
    {
        if(pre[num[i]+N]==-1)
          l=1;
        else
          l=pre[num[i]+N]+1;
        pre[num[i]+N]=i;
        Update(l,i,1,n,1,num[i]);
        sz=querys[i].size();
        for(int j=0;j<sz;++j)
        {
            tmp=querys[i][j];
            ans[tmp.pos]=Query(tmp.l,i,1,n,1);
        }
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,q;
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&num[i]);
            querys[i].clear();
        }
        build(1,n,1);
        scanf("%d",&q);
        int l,r;
        Node tmp;
        for(int i=0;i<q;++i)
        {
            scanf("%d%d",&l,&r);
            tmp.l=l;tmp.pos=i;
            querys[r].push_back(tmp);
        }
        solve(n);
        for(int i=0;i<q;++i)
          printf("%d\n",ans[i]);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值