SPOJ 1557 GSS2 - Can you answer these queries II (線段樹+維護歷史最值)
阿新 • • 發佈:2020-08-09
都說這題是 GSS 系列中最難的,今天做了一下,名副其實
首先你可以想到各種各樣的線上亂搞想法,線段樹,主席樹,平衡樹,等等,但發現都不太可行。
注意到題目也沒有說強制線上,因此可以想到離線地去解決這道題。
我們把詢問按照右端點從小到大排序。假設當前詢問的右端點為 \(i\)。
定義 \(s_j\) 為 \([j,i]\) 中不重複數字的和。我們建一棵線段樹維護 \(s_j\) 的最大值。
這樣修改起來就比較自然了,當右端點從 \(i-1\) 變到 \(i\) 的時候,記 \(a_i\) 上一次出現的位置為 \(k\),那麼這個 \(a_i\) 只會對 \(s_{k+1},s_{k+2},\dots,s_i\)
詢問也比較容易。詢問 \((l,i)\) 的答案就是 \(s_l,s_{l+1},\dots,s_i\) 的歷史最大值。
這樣原題就轉化為一道看起來比較可做的 DS 了,要你維護一個序列,支援:
- 區間加
- 區間歷史最大值
之前寫過一道類似的題題號什麼我忘了,可現在忘了怎麼做了/kk,只好看題解再推一遍。
線段樹區間維護四個東西 \(mx,hmx,lz,hlz\) 表示最大值,歷史最大值,區間加標記,歷史最大區間加標記(歷史出現過的懶標記的最大值)。
上推操作就不用說了吧,直接對 \(mx,hmx\) 取 \(\max\)
區間加上一個值 \(v\),那麼最大值肯定也會加上 \(v\),歷史最大值就和當前最大值取 \(\max\),懶標記也同理。
最複雜的就是如何下傳標記。
例如我們現在有一段操作序列 \(+1-3+4-2-1+5-2\),那麼此時懶標記為 \(+2\),歷史最大懶標記為 \(+4\)(\(+1-3+4-2-1+4\))。
然後它的兒子當前最大值為 \(2\),歷史最大值為 \(3\),當前懶標記為 \(-1\),歷史最大懶標記為 \(+1\)。
那麼:
- \(mx\) 顯然直接加上父親的懶標記即可,\(2+1-3+4-2-1+5-2=2+2=4\)。
- \(hmx\) 拿當前最大值 \(2\)
- \(lz\) 也是直接加上父親的 \(lz\) 即可,\(-1+2=+1\)
- \(hlz\) 也拿當前最大懶標記加上歷史最大懶標記 \(+4\)(\(-1+1-3-4-2-1+5=+3\)),與原來歷史最大懶標記 \(+1\) 比較。
最後,重中之重,下放懶標記的順序一定要注意。
/*
Contest: -
Problem: SP1557
Author: tzc_wk
Time: 2020.8.8
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a) a.begin(),a.end()
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,0x3f,sizeof(a))
#define fillsmall(a) memset(a,0xcf,sizeof(a))
#define y1 y1010101010101
#define y0 y0101010101010
#define int long long
typedef pair<int,int> pii;
inline int read(){
int x=0,neg=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*neg;
}
int n=read(),m,a[100005],ans[100005];
struct _query{
int l,r,id;
friend bool operator <(_query a,_query b){
return a.r<b.r;
}
} q[100005];
struct node{
int l,r,mx,hmx,lz,hlz;
} s[100005<<2];
inline void pushup(int k){
s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);
s[k].hmx=max(s[k<<1].hmx,s[k<<1|1].hmx);
}
inline void build(int k,int l,int r){
s[k].l=l;s[k].r=r;
if(l==r) return;
int mid=(l+r)>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
inline void pushdown(int k){
s[k<<1].hmx=max(s[k<<1].hmx,s[k<<1].mx+s[k].hlz);
s[k<<1|1].hmx=max(s[k<<1|1].hmx,s[k<<1|1].mx+s[k].hlz);
s[k<<1].mx+=s[k].lz;
s[k<<1|1].mx+=s[k].lz;
s[k<<1].hlz=max(s[k<<1].hlz,s[k<<1].lz+s[k].hlz);
s[k<<1|1].hlz=max(s[k<<1|1].hlz,s[k<<1|1].lz+s[k].hlz);
s[k<<1].lz=s[k<<1].lz+s[k].lz;
s[k<<1|1].lz=s[k<<1|1].lz+s[k].lz;
s[k].lz=s[k].hlz=0;
}
inline void add(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r){
s[k].mx+=x;s[k].hmx=max(s[k].hmx,s[k].mx);
s[k].lz+=x;s[k].hlz=max(s[k].hlz,s[k].lz);
return;
}
pushdown(k);
int mid=(s[k].l+s[k].r)>>1;
if(r<=mid) add(k<<1,l,r,x);
else if(l>mid) add(k<<1|1,l,r,x);
else add(k<<1,l,mid,x),add(k<<1|1,mid+1,r,x);
pushup(k);
}
inline int query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r){
return s[k].hmx;
}
pushdown(k);
int mid=(s[k].l+s[k].r)>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
map<int,int> mp;
signed main(){
fz(i,1,n) a[i]=read();
m=read();fz(i,1,m) q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+m+1);
build(1,1,n);
int cur=1;
fz(i,1,n){
add(1,mp[a[i]]+1,i,a[i]);mp[a[i]]=i;
while(cur<=m&&q[cur].r<=i){
ans[q[cur].id]=query(1,q[cur].l,q[cur].r);cur++;
}
}
fz(i,1,m) printf("%lld\n",ans[i]);
return 0;
}