您的位置:首页 > 产品设计 > UI/UE

SPOJ 1043 Can you answer these queries I (超强线段树)

2017-07-18 13:02 453 查看

GSS1 - Can you answer these queries I

[align=center]#tree[/align]

You are given a sequence A[1], A[2], ..., A
. ( |A[i]| ≤ 15007 , 1 ≤ N ≤ 50000 ). A query is defined as follows:

Query(x,y) = Max { a[i]+a[i+1]+...+a[j] ; x ≤ i ≤ j ≤ y }.

Given M queries, your program must output the results of these queries.

Input

The first line of the input file contains the integer N.
In the second line, N numbers follow.
The third line contains the integer M.
M lines follow, where line i contains 2 numbers xi and yi.

Output

Your program should output the results of the M queries, one query per line.

Example

Input:
3
-1 2 3
1
1 2
Output:
2


        题意简单明了,静态求区间最大连续子序列。
        想起之前做过的一道LCT的题目,是求链上染色区域个数,和这个有丝丝相似。
        首先,通过观察区间最大连续子序列和的构成,我们发现,某一个区间的最大连续子序列max的备选序列可以是它的左右子区间的最大连续子序列,或者是左子区间的最大右端连续子序列rmax与右子区间的最大左端连续子序列lmax的和。那么在一棵线段树中,我们需要维护的就是该区间的最大连续子序列max、最大左端连续子序列lmax和最大右端连续子序列rmax。
        维护max,刚刚已经说了,那么lmax和rmax如何维护呢。这个我也是错了几次后才弄懂的。首先,显然的lmax可以是继承它左子区间的lmax,但是如果说右子区间的lmax很大,那会不会影响总区间的lmax呢?答案是肯定的。对于lmax,它的大小是左子区间的lmax 与 左子区间和+右子区间lmax 中大的那个。即左端最大连续子区间可以是左子区间的lmax,也可以是整个左子区间的和加上有自取件的lmax。简单的例子就是:-1,2。区间[1,1]的lmax=-1,则区间[1,2]的lmax应该等于2-1=1,而不是-1。同理,右端连续最大也是这样维护的。这也就意味着我们还有维护一个区间和sum,这个就是非常基础的线段树操作了,在这里不再赘述。
        还有一个比较麻烦的地方就是查询了。即使写好了维护,查询也不是那么好理解的。因为查询的区间可能横跨两个子区间,这个时候又要仔细考虑了。同样,它可以是两个子区间中max大的那个,还可以是左子区间的rmax+右子区间的lmax,而要求这个lmax和rmax,则又要写两个函数去计算。然后在getlmax和getrmax的时候,要点和维护lmax和rmax的时候一样,返回的最优解可能也会跨一个子区间,所以说我们还要写一个getsum的函数计算区间总和。真够麻烦的,具体见代码:
#include<bits/stdc++.h>
#define LL long long
#define N 201000
using namespace std;

struct ST
{
LL a
;

struct node
{
LL lmax,rmax,max,sum,num;
int l,r;
} tree[N*4];

inline void push_up(int i)
{
tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
tree[i].max=max(tree[i<<1].max,tree[i<<1|1].max);			//max是三个备选解中大的那个
tree[i].max=max(tree[i].max,tree[i<<1].rmax+tree[i<<1|1].lmax);
tree[i].lmax=max(tree[i<<1].lmax,tree[i<<1].sum+tree[i<<1|1].lmax);			//lmax、rmax要跨区间比较
tree[i].rmax=max(tree[i<<1|1].rmax,tree[i<<1|1].sum+tree[i<<1].rmax);
}

inline void build(int i,int l,int r)
{
tree[i].r=r;
tree[i].l=l;
if (l==r)
{
tree[i].num=tree[i].rmax=a[l];
tree[i].lmax=tree[i].max=a[l];
tree[i].sum=a[l];
return;
}
LL mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
push_up(i);
}

inline LL getsum(int i,int l,int r)					//计算区间和
{
if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].sum;
LL mid=(tree[i].l+tree[i].r)>>1;
if (mid>=r) return getsum(i<<1,l,r);
else if (mid<l) return getsum(i<<1|1,l,r);
else return getsum(i<<1,l,mid)+getsum(i<<1|1,mid+1,r);
}

inline LL getrmax(int i,int l,int r)				//计算右端最大连续子序列
{
if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].rmax;
LL mid=(tree[i].l+tree[i].r)>>1;
if (mid>=r) return getrmax(i<<1,l,r);
else if (mid<l) return getrmax(i<<1|1,l,r);
else return max(getrmax(i<<1|1,mid+1,r),getrmax(i<<1,l,mid)+getsum(i<<1|1,mid+1,r));		//解可能跨区间
}

inline LL getlmax(int i,int l,int r)				//计算左端最大连续子序列
{
if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].lmax;
LL mid=(tree[i].l+tree[i].r)>>1;
if (mid>=r) return getlmax(i<<1,l,r);
else if (mid<l) return getlmax(i<<1|1,l,r);
else return max(getlmax(i<<1,l,mid),getlmax(i<<1|1,mid+1,r)+getsum(i<<1,l,mid));		//解可能跨区间
}

inline LL getmax(int i,int l,int r)				//计算区间最大子序列
{
if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].max;
LL mid=(tree[i].l+tree[i].r)>>1;
if (mid>=r) return getmax(i<<1,l,r);
else if (mid<l) return getmax(i<<1|1,l,r);
else
{
LL a=max(getmax(i<<1,l,mid),getmax(i<<1|1,mid+1,r));
LL b=getrmax(i<<1,l,mid)+getlmax(i<<1|1,mid+1,r);
return max(a,b);							//解可能是两个子区间值大的那个也可能是中间部分
}
}
} seg;

int n;

int main()
{
while(~scanf("%d",&n))
{
memset(seg.a,0,sizeof(seg.a));
for(int i=1;i<=n;i++)
scanf("%lld",&seg.a[i]);
seg.build(1,1,n);
int q; cin>>q;
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%lld\n",seg.getmax(1,x,y));
}
}
return 0;
}


    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: