您的位置:首页 > 其它

UVALive - 3938 Ray, Pass methe dishes!"(动态最大连续和子序列,线段树区间合并)

2018-03-10 17:29 525 查看
题意:    给出一个长度为n的整数序列D,你的任务是对m各询问做出回答。对于询问(a,b),需要找到两个下标x和y,使得a<=x<=y<=b,并且Dx+Dx+1+...+Dy尽量大。如果有多组满足条件的x和y,x应该尽量小。如果还有多解,y应该尽量小。 题解:      蓝书上的线段树练习题,做起来感觉还是不简单地,比起来前面做过的那些题,这种区间合并,多重递归的要复杂的多! 建立一颗线段树,每个节点维护以下信息: max_sub : 本节点维护的最大连续和的子区间[L,R]max_prefix : 本节点的最大前缀和区间的右端坐标Rmax_suffix :本节点的最大后缀和区间的左端坐标L   举个栗子:对于一个查询[20,50]     区间的最大连续和,由于区间被分成了[20,32],[33,50],结果只有以下三种情况: 1.   最大连续和在[20,32]中,即max_sub[20,32]2.   最大连续和在[33,50]中,即max_sub[33,50]3.   最大连续和x在[20,32]中,y在[33,50]中。也就是max_sum[20,50]=max_suffix[20,32]+max_prefix[33,50],并且更新x,y的坐标。 难点就在于还要在递归的去求max_prefix,max_suffix。  #include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<string>
#include<cstring>
#include<vector>
#include<functional>
#include<utility>
#include<set>
#include<map>
#include<cmath>
#include<cctype>
#define INF 0x3f3f3f3f

using namespace std;

#define lson 2*i,l,m
#define rson 2*i+1,m+1,r

const int maxn=500000+100;
typedef pair<int,int>pii;
pii max_sub[maxn<<2];
int max_prefix[maxn<<2],max_suffix[maxn<<2];

long long prefix_sum[maxn];
//后面有两种调用方式。
long long get_sum(int l,int r)
{
return prefix_sum[r]-prefix_sum[l-1];
}

long long get_sum(pii p)
{
return get_sum(p.first,p.second);
}

pii better(pii a,pii b)
{
if(get_sum(a)!=get_sum(b))return get_sum(a)>get_sum(b)?a:b;
return a<b? a:b;
}

void pushup(int i,int l,int r)
{
//计算最大和
max_sub[i]=better(max_sub[2*i],max_sub[2*i+1]);//直接从左边或者右边
max_sub[i]=better(max_sub[i],pii(max_suffix[2*i],max_prefix[2*i+1]));//左半边和右半边都有

//计算前缀
long long x=get_sum(l,max_prefix[2*i]);//前缀和来自前半边
long long y=get_sum(l,max_prefix[2*i+1]);//前缀和的右端点在右半边
if(x==y)max_prefix[i]= max_prefix[2*i];//两者相等,取前半边
else max_prefix[i]=x>y?max_prefix[2*i]:max_prefix[2*i+1];

//计算后缀
x=get_sum(max_suffix[i*2],r);
y=get_sum(max_suffix[2*i+1],r);
if(x==y) max_suffix[i]=max_suffix[2*i];
else max_suffix[i]=x>y?max_suffix[2*i]:max_suffix[2*i+1];

}

void build(int i,int l,int r)
{
if(l==r)
{
max_sub[i]=pii(l,r);
max_prefix[i]=max_suffix[i]=l;
return;
}

int m=(l+r)/2;
build(lson);
build(rson);
pushup(i,l,r);
}

pii query_prefix(int ql,int qr,int i,int l,int r)
{
if(max_prefix[i]<=qr)return pii(l,max_prefix[i]);

int m=(l+r)/2;
if(qr<=m)return query_prefix(ql,qr,lson);//干脆地去找左半区间
pii a=query_prefix(ql,qr,rson);//找右半区间的最大前缀,此时区间左边不是l了。
a.first=l;
return better(a,pii(l,max_prefix[2*i]));//
}

pii query_suffix(int ql,int qr,int i,int l,int r)
{
if(max_suffix[i]>=ql)return pii(max_suffix[i],r);

int m=(l+r)/2;
if(m<ql)return query_suffix(ql,qr,rson);

pii a=query_suffix(ql,qr,lson);
a.second=r;
return better(a,pii(max_suffix[2*i+1],r));
}

pii query(int ql,int qr,int i,int l,int r)
{
if(ql<=l&&qr>=r)return max_sub[i];

int m=(l+r)/2;
if(qr<=m)return query(ql,qr,lson);
if(ql>m)return query(ql,qr,rson);

int L=query_suffix(ql,qr,lson).first;
int R=query_prefix(ql,qr,rson).second;

pii a=better(query(ql,qr,lson),query(ql,qr,rson));//单独来自左半边或者右半边
return better(pii(L,R),a);
}

int main()
{
int n,m;
int kase=1;
while(cin>>n>>m&&n&&m)
{
prefix_sum[0]=0;
for(int i=1; i<=n; i++)
{
int x;
scanf("%d",&x);
prefix_sum[i]=prefix_sum[i-1]+x;
}

build(1,1,n);
printf("Case %d:\n",kase++);
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
pii a=query(x,y,1,1,n);
printf("%d %d\n",a.first,a.second);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: