"Ray, Pass me the dishes!"(动态最大连续和)(线段树)

"Ray, Pass me the dishes!"

题意:给出一个长度为n的序列D,对于m个询问(a,b)找到两个下标x,y使得a<=x<=y<=b,Da+...+Db尽量大,输出x可能小,y也可能小的结果。

思路:构造一颗线段树,每个结点维护3个值,最大连续和max_sub,最大前缀和max_perfix,最大后缀和max_suffix,最后只有3种情况。

1连续最大和都在左子树区间内

2连续最大和都在右子树区间内

3连续最大和在整个区间内(左子树的最大后缀+右子树的最大前缀)

具体实现看代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500005;
ll sum[N];
int ql,qr;
struct node{
	int max_perfix;
	int max_suffix;
	pair<int,int>max_sub;
}node[4*N];
ll cal_sum(int l,int r){
	return sum[r]-sum[l-1];
}
pair<int,int>cmp(pair<int,int>a,pair<int,int>b){
	if(cal_sum(a.first,a.second)>cal_sum(b.first,b.second))
		return a;
	else if(cal_sum(a.first,a.second)<cal_sum(b.first,b.second))
		return b;
	else if(a<b)
		return a;
	else return b;	
}
void build(int o,int l,int r){
	if(l==r){
		node[o].max_perfix=node[o].max_suffix=l;
		node[o].max_sub=make_pair(l,l);
		return;
	}
	int mid=(l+r)/2;
	build(o*2,l,mid);
	build(o*2+1,mid+1,r);
	ll x1,x2;
	x1=cal_sum(l,node[o*2].max_perfix);
	x2=cal_sum(l,node[o*2+1].max_perfix);
	if(x1>=x2)
		node[o].max_perfix=node[o*2].max_perfix;
	else node[o].max_perfix=node[o*2+1].max_perfix;
	
	x1=cal_sum(node[o*2].max_suffix,r);
	x2=cal_sum(node[o*2+1].max_suffix,r);
	if(x1>=x2)
		node[o].max_suffix=node[o*2].max_suffix;
	else node[o].max_suffix=node[o*2+1].max_suffix;
	
	node[o].max_sub=cmp(node[o*2].max_sub,node[o*2+1].max_sub);
	node[o].max_sub=cmp(node[o].max_sub,make_pair(node[o*2].max_suffix,node[o*2+1].max_perfix));
}
pair<int,int>query_perfix(int o,int l,int r){
	if(qr>=node[o].max_perfix)
		return make_pair(l,node[o].max_perfix);
	int mid=(l+r)/2;
	if(qr<=mid)
		return query_perfix(o*2,l,mid);
	pair<int,int>x;
	x=query_perfix(o*2+1,mid+1,r);
	x.first=l;
	return cmp(make_pair(l,node[o*2].max_perfix),x);
}

pair<int,int>query_suffix(int o,int l,int r){
	if(ql<=node[o].max_suffix)
		return make_pair(node[o].max_suffix,r);
	int mid=(l+r)/2;
	if(ql>mid)
		return query_suffix(o*2+1,mid+1,r);
	pair<int,int>x;
	x=query_suffix(o*2,l,mid);
	x.second=r;
	return cmp(make_pair(node[o*2+1].max_suffix,r),x);
}

pair<int,int>query(int o,int l,int r){
	if(ql<=l&&qr>=r)
		return node[o].max_sub;
	int mid=(l+r)/2;
	if(qr<=mid)
		return query(o*2,l,mid);
	if(ql>mid)
		return query(o*2+1,mid+1,r);
	pair<int,int>x1,x2,x3;
	x1=query_suffix(o*2,l,mid);
	x2=query_perfix(o*2+1,mid+1,r);
	x3=cmp(query(o*2,l,mid),query(o*2+1,mid+1,r));
	return cmp(make_pair(x1.first,x2.second),x3);
}
int main(){
	int n,m;
	int cnt=1;
	while(~scanf("%d%d",&n,&m)){
		int x;
		sum[0]=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&x);
			sum[i]=sum[i-1]+x;
		}
		build(1,1,n);
		printf("Case %d:\n",cnt++);
		for(int i=0;i<m;i++){
			scanf("%d%d",&ql,&qr);
			pair<int,int>ans=query(1,1,n);
			printf("%d %d\n",ans.first,ans.second);
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值