SP1557 GSS2 - Can you answer these queries II

link

题目大意:

给一个 \(n\) 个元素的序列,\(q\) 次询问 \([l_i,r_i]\) 的最大子段和(相同元素只算一个)。

\(n,q \le 10^5,- 10^5\le a_i \le 10^5\).

解法:

首先考虑最大子段和的经典动态解法:维护 \(pre_i,suf_i,sum_i,mxsum_i\) 。这个时候你会发现无法合并。

Tips:对于区间询问问题,在没有思路时将其离线是一个很好的选择。

考虑离线问题并按右端点为第一关键字,右端点为第二关键字排序。用一个指针在数组中从左到右滑动,并同时加入滑动到的新点

那么加入一个新点的具体操作是什么呢?

我们考虑一个指针指向的元素为 \(j\),那么加入 \(j\) 可能会对哪些区间 \([i,j]\) 产生贡献呢?

显然是 \([lst_j+1,j]\)\(lst_j\) 表示左边最后一个与 \(a_j\) 相等的下标)。

联想到我们对一个静态序列求最大子段和的贪心算法,这个加点操作可以转化为在一些序列中加入一个新的元素,并同时更新这些序列的最大子段和,最后取所有序列中最大值为答案。

具体来说:

对于每个左端点 \(i\) ,如果 \(i\)可能与它连接并产生贡献的点连接成为一个序列,那么第 \(k\) 个询问答案实际上就是以 \([l_i,r_i]\) 中所有每个点开头组成的序列的最大子段和的最大值。

那么我们用一颗线段树来维护这些信息即可。

此时每个序列的最大子段和其实就是更新时的区间和的历史最大值。

code:

#include<bits/stdc++.h>
#define int long long 
using namespace std;

const int N=1e5+10,INF=1e9+7;
int n,m,a[N],lst[N],ans[N];
map<int,int>ptr;
struct Segment_tree{
	#define ls (o<<1)
	#define rs (o<<1|1)
	#define mid ((l+r)>>1)
	int tmx[N<<2],tsum[N<<2],htag[N<<2],stag[N<<2];
	void pushup(int o){
		tsum[o]=max(tsum[ls],tsum[rs]);
		tmx[o]=max(tmx[ls],tmx[rs]);
	}
	void pushdown(int o){
		tmx[ls]=max(tmx[ls],tsum[ls]+htag[o]);tmx[rs]=max(tmx[rs],tsum[rs]+htag[o]);
		tsum[ls]+=stag[o];tsum[rs]+=stag[o];
		htag[ls]=max(htag[ls],stag[ls]+htag[o]);htag[rs]=max(htag[rs],stag[rs]+htag[o]);
		stag[ls]+=stag[o];stag[rs]+=stag[o];
		stag[o]=htag[o]=0;
	}
	void add(int o,int l,int r,int s,int t,int k){
		if(s<=l&&r<=t){
			tsum[o]+=k;tmx[o]=max(tmx[o],tsum[o]);
			stag[o]+=k;
			htag[o]=max(htag[o],stag[o]);
			return;
		}
		pushdown(o);
		if(s<=mid)add(ls,l,mid,s,t,k);
		if(mid<t)add(rs,mid+1,r,s,t,k);
		pushup(o);
		return;
	}
	int querytmax(int o,int l,int r,int s,int t){
		if(s<=l&&r<=t)return tmx[o];
		int ret=-INF;
		pushdown(o);
		if(s<=mid)ret=querytmax(ls,l,mid,s,t);
		if(mid<t)ret=max(ret,querytmax(rs,mid+1,r,s,t));
		return ret;
	}
}tree;
struct query{
	int l,r;
	int id;
}q[N];

bool cmp(struct query q1,struct query q2){
	if(q1.r!=q2.r)return q1.r<q2.r;
	return q1.l<q2.l;
}

signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);                                      
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		lst[i]=ptr[a[i]];
		ptr[a[i]]=i;
	}
	cin>>m;
	for(int i=1;i<=m;i++)cin>>q[i].l>>q[i].r,q[i].id=i;
	sort(q+1,q+m+1,cmp);
	for(int i=1;i<=m;i++){
		for(int j=q[i-1].r+1;j<=q[i].r;j++)tree.add(1,1,n,lst[j]+1,j,a[j]);
		ans[q[i].id]=tree.querytmax(1,1,n,q[i].l,q[i].r);
	}
	for(int i=1;i<=m;i++)cout<<ans[i]<<"\n";
	
	return 0;
}
posted @ 2024-04-25 13:19  Little_corn  阅读(7)  评论(0编辑  收藏  举报