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); }