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

来源:互联网 发布:mac 中文字体 ttf 编辑:程序博客网 时间:2024/06/05 11:23

GSS1 - Can you answer these queries I

#tree

You are given a sequence A[1], A[2], ..., A[N] . ( |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 311 2Output: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 201000using namespace std;struct ST{LL a[N];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;}

    
阅读全文
0 0