SP1557 GSS2 - Can you answer these queries II

琢磨了很久的一题,经过历史考试的思考,终于懂了。
现在建立一个如下模型:
sum[1]=a[1]+a[2]+a[3]+a[4]+…+a[i]
sum[2]=a[2]+a[3]+a[4]+…+a[i]
sum[3]=a[3]+a[4]+…+a[i]
sum[i]=a[i] (假设a[1]-a[i]均不等)
然后,需要我们求[1,i]的最大子段和,这不就是求sum[1]-sum[i]的最大值吗?确实是这样的。 可能会问,题目不是说a[1]-a[i]有可能相等嘛,给我一个均不等的样例有什么用呢? 因为我们可以把一长段的有相同元素的数列分为若干段在段内无相同元素的数列。
为了快速进行累加,可以通过线段树来实现维护,对于以上的那个模型来说,就是add(1,1,n,1,i,a[i])。
对于整个数列来说,将每个位置的数字i,找到前一个与它相同的数字的位置,记录为pre[i],然后,我们就可以把[pre[i]+1,i]这个区间内所有的数都累加上a[i]了。
通过累加后我们会发现,对于某个叶子节点的T[k].sum来说,这个值就是:a[i]+a[i+1]+a[i+2]+…+a[n],除去里面相同的元素。那么它的最长子段和,不就是维护一个历史最大值吗?
所以这题,就成功转化为了:区间修改+查询区间历史最大值裸题了。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
int a[N],pre[N],pos[N<<1],ans[N];
struct number{int x,y,id;}num[N];
struct node{int sum,max,tagsum,tagmax;}T[N<<2];

inline bool cmp(number a,number b)
{
	return a.y<b.y;
}

inline void pushdown(int k)
{
	T[k<<1].max=max(T[k<<1].max,T[k<<1].sum+T[k].tagmax);
	T[k<<1].sum+=T[k].tagsum;
	T[k<<1].tagmax=max(T[k<<1].tagmax,T[k<<1].tagsum+T[k].tagmax);
	T[k<<1].tagsum+=T[k].tagsum; 
	
	T[k<<1|1].max=max(T[k<<1|1].max,T[k<<1|1].sum+T[k].tagmax);
	T[k<<1|1].sum+=T[k].tagsum;
	T[k<<1|1].tagmax=max(T[k<<1|1].tagmax,T[k<<1|1].tagsum+T[k].tagmax);
	T[k<<1|1].tagsum+=T[k].tagsum; 
	
	T[k].tagsum=0; T[k].tagmax=0;
}

void change(int k,int l,int r,int qx,int qy,int v)
{
	if (qx<=l && r<=qy)
	{
		T[k].sum+=v;
		T[k].max=max(T[k].max,T[k].sum);
		T[k].tagsum+=v;
		T[k].tagmax=max(T[k].tagmax,T[k].tagsum); 
		return;
	}
	pushdown(k);
	int mid=l+r>>1;
	if (qx<=mid) change(k<<1,l,mid,qx,qy,v);
	if (mid<qy) change(k<<1|1,mid+1,r,qx,qy,v);
	T[k].sum=max(T[k<<1].sum,T[k<<1|1].sum);
	T[k].max=max(T[k<<1].max,T[k<<1|1].max);
}

int query(int k,int l,int r,int qx,int qy)
{
	if (qx<=l && r<=qy) return T[k].max;
	pushdown(k);
	int mid=l+r>>1;
	int res=-2e9;
	if (qx<=mid) res=max(res,query(k<<1,l,mid,qx,qy));
	if (mid<qy) res=max(res,query(k<<1|1,mid+1,r,qx,qy));
	return res;
}

int main(){
	scanf("%d",&n);
	for (register int i=1; i<=n; ++i) 
	{
		scanf("%d",&a[i]);
		pre[i]=pos[a[i]+(int)1e5];
		pos[a[i]+(int)1e5]=i;	
	}
	scanf("%d",&m);
	for (register int i=1; i<=m; ++i) scanf("%d%d",&num[i].x,&num[i].y),num[i].id=i;
	sort(num+1,num+m+1,cmp);
	int j=1;
	for (register int i=1; i<=n; ++i)
	{
		change(1,1,n,pre[i]+1,i,a[i]);
		while (j<=m && num[j].y<=i) ans[num[j].id]=query(1,1,n,num[j].x,num[j].y),j++;
	}
	for (register int i=1; i<=m; ++i) printf("%d\n",ans[i]);
return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值