[SPOJ GSS2] Can you answer these queries II [线段树]

27 篇文章 0 订阅
17 篇文章 0 订阅

给定一个序列,查询区间子段去除重复元素的和的最大值。即给x,y,求sum=a[i]+a[i+1]+...+a[j],x<=i<=j<=y的最大的sum,但如果其中a[p]==a[q],i<=p<=q<=j,则计算和的时候a[p]和a[q]只计算一次。

这道题看起来和GSS1相似,但是做法完全不同。

使用数组lx[i]记录a[i]左边第一个等于a[i]的元素的位置+1。

离线处理所有的询问,将其按照右端点排序。

从左向右扫描数列,当扫描到a[i]时,线段树中第j个节点记录从j到i的去除重复元素的和,以及从j到i的区间子段去除重复元素的和的最大值。

即每扫描到一个新的数a[i],令数列b[lx[i]], b[lx[i]+1], ... , b[i]加上a[i],用线段树记录b[i]的区间历史最大值。

#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#include <iostream>

using namespace std;

struct Query {
	int l,r,i;
	friend bool operator < (const Query &a,const Query &b) {
		return a.r<b.r;
	}
};
struct SegNode {
	SegNode *ls,*rs;
	long long maxv,permaxv,maxlazy,lazy;
	void down() {
		if (ls) {
			ls->maxlazy=max(ls->maxlazy,ls->lazy+maxlazy);
			ls->lazy+=lazy;
		}
		if (rs) {
			rs->maxlazy=max(rs->maxlazy,rs->lazy+maxlazy);
			rs->lazy+=lazy;
		}
		permaxv=max(permaxv,maxv+maxlazy);
		maxv+=lazy;
		lazy=maxlazy=0;
	}
	void repair() {
		ls->down();rs->down();
		maxv=max(ls->maxv,rs->maxv);
		permaxv=max(ls->permaxv,rs->permaxv);
		maxlazy=lazy=0;
	}
};
SegNode node[200000];
SegNode *root,*an;

void print() {
	for (SegNode *i=node;i!=an;i++) {
		printf("Node %d:\n",(int)(i-node));
		printf(" ls:%d rs:%d\n",(int)(i->ls-node),(int)(i->rs-node));
		printf(" maxv:%lld permaxv:%lld lazy:%lld maxlazy:%lld\n",i->maxv,i->permaxv,i->lazy,i->maxlazy);
	}
	printf("\n");
}
SegNode *maketree(int l,int r) {
	SegNode *ans=an++;
	if (l==r) {
		ans->ls=ans->rs=NULL;
	} else {
		int t=(l+r)/2;
		ans->ls=maketree(l,t);
		ans->rs=maketree(t+1,r);
	}
	ans->maxlazy=ans->lazy=0;
	ans->maxv=ans->permaxv=0;
	return ans;
}
void set(SegNode *from,int l,int r,int ll,int rr,int x) {
	//printf("%d %d %d %d\n",(int)(from-node),ll,rr,x);
	from->down();
	if (l==ll&&r==rr) {
		from->lazy=x;
		from->maxlazy=max(x,0);
	} else {
		int t=(l+r)/2;
		if (rr<=t) {
			set(from->ls,l,t,ll,rr,x);
		} else if (ll>t) {
			set(from->rs,t+1,r,ll,rr,x);
		} else {
			set(from->ls,l,t,ll,t,x);
			set(from->rs,t+1,r,t+1,rr,x);
		}
		from->repair();
	}
}
int get(SegNode *from,int l,int r,int ll,int rr) {
	from->down();
	if (l==ll&&r==rr) return from->permaxv;
	int t=(l+r)/2;
	if (rr<=t) 
		return get(from->ls,l,t,ll,rr);
	else if (ll>t) 
		return get(from->rs,t+1,r,ll,rr);
	else 
		return max(get(from->ls,l,t,ll,t),get(from->rs,t+1,r,t+1,rr));
}

map<int,int>last;
int n;
int a[100001];
int lx[100001];
Query b[100000];
int ans[100000];

int main() {
	int i,x;
	scanf("%d",&n);
	for (i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		lx[i]=last[a[i]]+1;
		last[a[i]]=i;
	}
	an=node;
	root=maketree(1,n);
	int q;
	scanf("%d",&q);
	for (i=0;i<q;i++) {
		scanf("%d%d",&b[i].l,&b[i].r);
		b[i].i=i;
	}
	sort(b,b+q);
	x=1;
	for (i=0;i<q;i++) {
		for (;x<=b[i].r;x++) {
			set(root,1,n,lx[x],x,a[x]);
			//print();
			//getchar();
		}
		ans[b[i].i]=get(root,1,n,b[i].l,b[i].r);
	}
	for (i=0;i<q;i++) {
		printf("%d\n",ans[i]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值