LA3938-Ray, Pass me the dishes!--动态最大连续区间和(线段树+前后缀数和组)

更新:之前的写法那么挫居然也过了。。。缝缝改改最后从1.4S改成700ms了。。。主要改的地方是两处 ,见注释

修改后的代码放在最后面(700ms) 

最后重xie(400ms)


题意:给一个数组,求任意区间的最大连续和 长度n,查询次数m  n,m大小是5*10^5

每个数不超过1e9

之前做的是静态数组求一次最大连续和,方法有很多,dp之类的都能o(n)解决。  

还有一个是分治法,nlogn解决:http://blog.csdn.net/viphong/article/details/48221429

这里也是用分治法的思路解决:

称区间【l,r】的最大连续区间和为st_max[l,r]

对查询区间(a,b)

int mid=(a+b)/2;

要求st_max的话, 有三种情况:

情况1: st_max的起点和终点都在【a,mid】

情况2:st_max的起点和终点都在【mid+1,b】

情况3:st_max的起点在【a,mid】,终点在【mid+1,b】

对于情况三 : st_max= max_suffix(a,mid)+max_prefix(mid+1,b)  【容易理解】

其中max_suffix为一区间的最大后缀和

其中max_prefix为一区间的最大前缀和

所以,任意区间的st_max 都可求得。只要不断递归下去就可以了。 【线段树维护区间的结果】

而对于max_suffix、max_prefix,我们也是用分治的方法来求

例如 要求max_prefix【a,b】

那么先求max_prefix【a,mid】和max_prefix【mid+1,b】

然后选 max_prefix【a,mid】 和max_prefix【mid+1,b】+sum[mid] 中 较大的一个 即为 【a,b】的最大前缀和,其中sum【i】为前缀和数组

同理也可以分治求得max_suffix

-------------------------------------------------

最终是 用 两个线段树分别维护 max_prefix、max_suffix

再用一棵线段树维护最大连续和

建树nlogn

每次查询logn...常数应该比较大....所以跑了1.4S。。。犇都是0.x秒


1、要注意的是inf不能设为2^63-1 会溢出

2、维护某个最大连续和对应的始末位置 我是另创了一个node来记录

3、在情况三下,用query_max查找最大前/后缀和的时候,

查询的l,应该是当前区间的l和要查询的区间的l中 较大的一个

查询的r,应该是当前区间的r和要查询的区间的r中 较小的一个


 重写了一次代码。。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>

#define lson l , m , i << 1
#define rson m + 1 , r , i << 1 | 1
using namespace std;
typedef long long ll;
const ll inf=1223372036854775807;
struct node
{
    ll v,l,r;
    node() {}
    node(ll a,ll b,ll c)
    {
        v=a,l=b,r=c;
    }
    bool operator<(const node b) const
    {
        if (v!=b.v) return v<b.v;
        if (l!=b.l) return l>b.l;
        return  r>b.r;
    }
};
const int N =  500005+50;

ll aa[N];
ll psum[N],ok;
node sub[4*N],pre[4*N],suf[4*N];
void push_pre(int i,int l,int r)
{
    int mid=(l+r)>>1;
    node r1(psum[mid]-psum[l-1]+pre[i<<1|1].v,l,pre[i<<1|1].r);
    pre[i]= max(r1,pre[i<<1]);
}
void push_suf(int i,int l,int r)
{
    int mid=(l+r)>>1;
    node r1(psum[r]-psum[mid]+suf[i<<1].v,suf[i<<1].l,r);
    suf[i]= max(r1,suf[i<<1|1]);
}
void pushup_ans(int i,int l,int r)
{
    node r1(suf[i<<1].v+pre[i<<1|1].v,suf[i<<1].l,pre[i<<1|1].r);
    r1=max(r1,sub[i<<1]);
    sub[i]= max(r1,sub[i<<1|1]);
}
void build(int l,int r,int i)    //  线段树的建立;
{
    if(l==r)
    {
        sub[i].l=pre[i].l=suf[i].l=l;
        sub[i].r=pre[i].r=suf[i].r=r;
        sub[i].v=pre[i].v=suf[i].v=aa[++ok];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,i<<1);
    build(mid+1,r,i<<1|1);
    push_pre(i,l,r);
    push_suf(i,l,r);
    pushup_ans(i,l,r);
}
node query_pre(int qL,int qR,int l,int r,int i)
{
    if (qL <= l && r <= qR)
        return pre[i];
    int m = (l + r) >> 1;
    node  ret1(-inf,l,m),ret2(-inf,m+1,r);
    if (qR <= m) return  query_pre(qL , qR , lson);
    if (qL > m) return  query_pre(qL , qR , rson);
    ret1 = query_pre(qL , qR , lson),ret2 = query_pre(qL , qR , rson);
    int LL=max(qL,l),RR=min(qR,r);
    node r1(psum[m]-psum[LL-1]+ret2.v,LL,ret2.r);
    return max(ret1,r1);
}
node query_suf(int qL,int qR,int l,int r,int i)
{
    if (qL <= l && r <= qR)
        return suf[i];
    int m = (l + r) >> 1;
    node ret1(-inf,l,m),ret2(-inf,m+1,r);
    if (qR <= m) return  query_suf(qL , qR , lson);
    if (qL > m) return  query_suf(qL , qR , rson);
    ret1 = query_suf(qL , qR , lson),ret2 = query_suf(qL , qR , rson);
    int LL=max(qL,l),RR=min(qR,r);
    node r1(psum[RR]-psum[m]+ret1.v,ret1.l,RR);
    return max(ret2,r1);
}

node query_max(int qL,int qR,int l,int r,int i) //L,R为为查询区间,lr当前区间
{
    if (qL <= l && r <= qR)
        return sub[i];
    int m = (l + r) >> 1;
    node  ret1(-inf,l,m),ret2(-inf,m+1,r);
    if (qR <= m) return  query_max(qL , qR , lson);
    if (qL > m)  return query_max(qL , qR , rson);
    ret1= query_max(qL , qR , lson),ret2= query_max(qL , qR , rson);

    node p1=query_pre(qL , qR , rson);
    node s1=query_suf(qL , qR , lson);
    node ret(p1.v+s1.v,s1.l,p1.r);
    return max(ret,max(ret1,ret2));
}
int main()
{
      //freopen("in.txt","r",stdin);
     //freopen("out1.txt","w",stdout);
    int cnt=1;
    ll n ,a,b,i,m;
    while(scanf("%lld%lld", &n,&m)!=EOF)
    {
        for(  i = 1; i <= n; i++)
        {
            scanf("%lld",&aa[i]);
            psum[i]=aa[i]+psum[i-1];
        }
        // for(  i = 1; i <= n; i++)
        //       update(1,1,n,i,i,psum[i]);
        ok=0;
        build(1,n,1);

        int l,r;
        printf("Case %d:\n",cnt++);
        for (int i=1; i<=m; i++)
        {
            scanf("%d%d",&l,&r);
            node ans=query_max(l,r,1,n,1);
            printf("%lld %lld\n",ans.l,ans.r);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值