SPOJ GSS1 - Can you answer these queries I

题面

题意

给出一串数,每次询问给出l,r,求其中满足l<=i<=j<=r时,a[i]+a[i+1]……a[j]的最大值.

做法

用线段树来做,不难发现要想求出线段树的每个节点的最大子串和十分容易,难点就在于区间的合并.
可以对于每个线段树的上的点所维护的区间[l,r]记录3个值,其中令l<=i<=j<=r
1.sum
2.maxl:[l,i]的最大值
3.maxl.[i,r]的最大值
4.mx:[i,j]的最大值
这样就可以合并区间a,b了:
1.new.sum=a.sum+b.sum
2.new.maxl=max(a.maxl,a.sum+b.lmax)
3.new.maxr=max(b.maxr,b.sum+s.maxr)
4.new.mx=max(a.mx,b.mx,a.maxr+b.maxl)
每次询问找到组成这段区间的点后合并即可

代码

#include<iostream>
#include<cstdio>
#include<vector>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
#define mid ((l+r)>>1)
#define N 50010
using namespace std;

ll n,m,num[N],tt=1;
struct Node
{
    ll ls,rs,sum,lmax,rmax,mx;
}node[N<<1];
vector<ll>use;

inline Node hb(Node &res,Node u,Node v)
{
    res.sum=u.sum+v.sum;
    res.lmax=max(u.lmax,u.sum+v.lmax);
    res.rmax=max(v.rmax,v.sum+u.rmax);
    res.mx=max(max(u.mx,v.mx),u.rmax+v.lmax);
    return res;
}

void build(ll now,ll l,ll r)
{
    if(l==r)
    {
        node[now].sum=node[now].lmax=node[now].rmax=node[now].mx=num[l];
        return;
    }
    if(l<=mid)
    {
        node[now].ls=++tt;
        build(tt,l,mid);
    }
    if(mid<r)
    {
        node[now].rs=++tt;
        build(tt,mid+1,r);
    }
    hb(node[now],node[node[now].ls],node[node[now].rs]);
}

void ask(ll now,ll l,ll r,ll u,ll v)
{
    if(l==u&&v==r)
    {
        use.push_back(now);
        return;
    }
    if(u<=mid) ask(node[now].ls,l,mid,u,min(mid,v));
    if(v>mid) ask(node[now].rs,mid+1,r,max(u,mid+1),v);
}

inline ll work(ll u,ll v)
{
    ll i,j;
    Node tmp;
    use.clear();
    ask(1,1,n,u,v);
    tmp=node[use[0]];
    for(i=1;i<use.size();i++)
    {
        hb(tmp,tmp,node[use[i]]);
    }
    return tmp.mx;
}

int main()
{
    ll i,j,p,q;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&num[i]);
    }
    build(1,1,n);
    cin>>m;
    for(i=1;i<=m;i++)
    {
        scanf("%lld%lld",&p,&q);
        printf("%lld\n",work(p,q));
    }
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/80244104
今日推荐