1. 程式人生 > 實用技巧 >SPOJ 1557 GSS2 - Can you answer these queries II (線段樹+維護歷史最值)

SPOJ 1557 GSS2 - Can you answer these queries II (線段樹+維護歷史最值)

都說這題是 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\)

產生貢獻,也就是說我們線上段樹上 \([k+1,i]\) 位置上的數 \(+a_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\)
    加上歷史最大懶標記 \(+4\)\(2+1-3-4-2-1+5=6\)),與原來歷史最大值 \(3\) 比較。
  • \(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;
}