bzoj 2482: [Spoj GSS2] Can you answer these queries II 线段树

2482: [Spoj1557] Can you answer these queries II

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 145  Solved: 76
[Submit][Status][Discuss]

Description

给定n个元素的序列。
给出m个询问:求l[i]~r[i]的最大子段和(可选空子段)。
这个最大子段和有点特殊:一个数字在一段中出现了两次只算一次。
比如:1,2,3,2,2,2出现了3次,但只算一次,于是这个序列的和是1+2+3=6。

Input

 

第一行一个数n。
第二行n个数,为给定的序列,这些数的绝对值小于等于100000。
第三行一个数m。
接下来m行,每行两个数,l[i],r[i]。

Output

M行,每行一个数,为每个询问的答案。

 

Sample Input

9
4 -2 -2 3 -1 -4 2 2 -6
3
1 2
1 5
4 9


Sample Output


4
5
3

HINT

【数据说明】

30%:1 <= n, m <= 100

100%:1 <= n, m <= 100000

 

  一年前抄标程把spoj的gss2做了,一年后重新做了一遍,仍然把我恶心的。。。。【话说这道题没卡longlong很不爽】

  还是一步一步想吧,首先是离线,转成线段树的区间加,询问区间最大值的历史最大值。

  这东西怎么搞呢?

  我们先不考虑下放标记的问题,单考虑一个结点。

  记录plus[]表示从上次清除tag到现在,已经有多少“加”操作下降到当前点。

  记录hplus[]表示从上次清除tag到现在,历史加操作“峰值”与plus的差值。

  在不考虑下放的情况下每次加v操作及plus+=v,hplus=max(0,hplus-v);

  当然作为线段树,我们还要记录maxv表示当前区间的答案,及区间历史最大值。

  然后再考虑标记下方问题。

  在调试中发现单纯maxv很难维护,需要在加一个中间变量,视为nmaxv,及当前最大值。

  转移什么的知道变量含义就可以参考代码了。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<assert.h>
using namespace std;
#define MAXN 110000
#define MAXQ MAXN
#define MAXT MAXN*5
#define lch (now<<1)
#define rch (now<<1^1)
#define smid ((l+r)>>1)
#define INF 0x3f3f3f3f
typedef long long qword;
struct sgt_node
{
        qword plus,maxv;
        qword hplus;
        qword nmaxv;
}sgt[MAXT];
void make_plus(int now,qword v)
{
        sgt[now].hplus-=v;
        sgt[now].plus+=v;
        sgt[now].hplus=max(sgt[now].hplus,0ll);
        sgt[now].nmaxv+=v;
        sgt[now].maxv=max(sgt[now].maxv,sgt[now].nmaxv+sgt[now].hplus);
}
void down(int now)
{
        make_plus(lch,sgt[now].plus+sgt[now].hplus);
        make_plus(lch,-sgt[now].hplus);
        make_plus(rch,sgt[now].plus+sgt[now].hplus);
        make_plus(rch,-sgt[now].hplus);
        sgt[now].plus=sgt[now].hplus=0;
        assert(sgt[now].maxv==max(sgt[lch].maxv,sgt[rch].maxv));
}
void Build_sgt(int now,int l,int r)
{
        sgt[now].plus=0;
        sgt[now].hplus=0;
        sgt[now].maxv=0;
        if (l==r)return ;
        Build_sgt(lch,l,smid);
        Build_sgt(rch,smid+1,r);
}
void Add_sgt(int now,int l,int r,int x,int y,qword v)
{
        if (l==x && r==y)
        {
                make_plus(now,v);
                return ;
        }
        down(now);
        if (y<=smid)
                Add_sgt(lch,l,smid,x,y,v);
        else if (smid<x)
                Add_sgt(rch,smid+1,r,x,y,v);
        else 
        {
                Add_sgt(lch,l,smid,x,smid,v);
                Add_sgt(rch,smid+1,r,smid+1,y,v);
        }
        sgt[now].nmaxv=max(sgt[lch].nmaxv,sgt[rch].nmaxv);
        sgt[now].maxv=max(sgt[lch].maxv,sgt[rch].maxv);
}
qword Query_sgt(int now,int l,int r,int pos)
{
        if (l==r)
                return sgt[now].maxv+sgt[now].plus+sgt[now].hplus;
        down(now);
        if (pos<=smid)
                return Query_sgt(lch,l,smid,pos);
        else 
                return Query_sgt(rch,smid+1,r,pos);
}
int Query_sgt(int now,int l,int r,int x,int y)
{
        if (l==x && r==y)
                return sgt[now].maxv;
        down(now);
        if (y<=smid)
                return Query_sgt(lch,l,smid,x,y);
        else if (smid<x)
                return Query_sgt(rch,smid+1,r,x,y);
        else
                return max(Query_sgt(lch,l,smid,x,smid),Query_sgt(rch,smid+1,r,smid+1,y));
}
int a[MAXN],lastid[MAXN*2],prv[MAXN];
struct qur_t
{
        int l,r,id;
        qword ans;
}qur[MAXQ];
bool cmp_r(qur_t q1,qur_t q2)
{
        return q1.r<q2.r;
}
bool cmp_id(qur_t q1,qur_t q2)
{
        return q1.id<q2.id;
}
int main()
{
        freopen("input.txt","r",stdin);
//        freopen("output.txt","w",stdout);
        int n,m;
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
                scanf("%d",a+i);
                prv[i]=lastid[a[i]+MAXN];
                lastid[a[i]+MAXN]=i;
        }
        scanf("%d",&m);
        for (int i=0;i<m;i++)
        {
                scanf("%d%d",&qur[i].l,&qur[i].r);
                qur[i].id=i;
        }
        sort(qur,qur+m,cmp_r);
        int qn=0;
        for (int i=1;i<=n;i++)
        {
            //    fprintf(stderr,"%d\n",i);
                Add_sgt(1,1,n,prv[i]+1,i,a[i]);
                while (qn<m && qur[qn].r==i)
                {
                        qur[qn].ans=Query_sgt(1,1,n,qur[qn].l,qur[qn].r);
                        qn++;
                }
        }
        sort(qur,qur+m,cmp_id);
        for (int i=0;i<m;i++)
                printf("%lld\n",qur[i].ans);
}

 

转载于:https://www.cnblogs.com/mhy12345/p/4442736.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值