线段树,分治法(动态最大连续和,LA 3938)

长代码找手误真的难= =。很容易脑补成对的,然后就混过去了。其实不应该贪快嫌麻烦,一定要想清楚。


线段树维护:区间左坐标,区间右坐标,区间和,最大连续和,最大连续和左坐标,最大连续和右坐标,最大前缀和,最大前缀和右坐标,最大后缀和,最大后缀和左坐标。


如果区间和相等,都是在保证左坐标最小的情况下,右坐标最小。


可以证明当区间和相等时,选择右边>左+右>选择左边。


以前一直都没用结构体,而且递归时询问区间都不变的(就是只会水题),所以搞了好久。


代码

#include<bits/stdc++.h>
#define maxn 500010
using namespace std;
typedef long long ll;

ll n,m;
ll a[maxn];

struct Node
{
    ll l,r,sub,subl,subr,pre,prer,suf,sufl,sum;
};

Node tree[maxn<<2];

void BUILD(ll l,ll r,ll now)
{
    tree[now].l=l;
    tree[now].r=r;

    if(l==r)
    {
        tree[now].sum=tree[now].sub=tree[now].pre=tree[now].suf=a[l];
        tree[now].subl=tree[now].subr=tree[now].prer=tree[now].sufl=l;
        return;
    }
    ll m=l+(r-l)/2;
    BUILD(l,m,now<<1);
    BUILD(m+1,r,now<<1|1);

    tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
    tree[now].sub=tree[now<<1|1].sub;
    tree[now].subl=tree[now<<1|1].subl;
    tree[now].subr=tree[now<<1|1].subr;
    if(tree[now<<1].suf+tree[now<<1|1].pre>=tree[now].sub)
    {
        tree[now].sub=tree[now<<1].suf+tree[now<<1|1].pre;
        tree[now].subl=tree[now<<1].sufl;
        tree[now].subr=tree[now<<1|1].prer;
    }
    if(tree[now<<1].sub>=tree[now].sub)
    {
        tree[now].sub=tree[now<<1].sub;
        tree[now].subl=tree[now<<1].subl;
        tree[now].subr=tree[now<<1].subr;
    }

    if(tree[now<<1].pre>=tree[now<<1].sum+tree[now<<1|1].pre)
    {
        tree[now].pre=tree[now<<1].pre;
        tree[now].prer=tree[now<<1].prer;
    }
    else
    {
        tree[now].pre=tree[now<<1].sum+tree[now<<1|1].pre;
        tree[now].prer=tree[now<<1|1].prer;
    }

    if(tree[now<<1|1].suf>tree[now<<1|1].sum+tree[now<<1].suf)
    {
        tree[now].suf=tree[now<<1|1].suf;
        tree[now].sufl=tree[now<<1|1].sufl;
    }
    else
    {
        tree[now].suf=tree[now<<1|1].sum+tree[now<<1].suf;
        tree[now].sufl=tree[now<<1].sufl;
    }
}

Node Q(ll l,ll r,ll now)
{
    if(l==tree[now].l&&r==tree[now].r) return tree[now];
    ll m=tree[now].l+(tree[now].r-tree[now].l)/2;
    if(r<=m) return Q(l,r,now<<1);
    if(l>m) return Q(l,r,now<<1|1);
    Node ans;
    Node ansl=Q(l,m,now<<1);
    Node ansr=Q(m+1,r,now<<1|1);
    ans.l=l;
    ans.r=r;
    ans.sum=ansl.sum+ansr.sum;

    ans.sub=ansr.sub;
    ans.subl=ansr.subl;
    ans.subr=ansr.subr;
    if(ansl.suf+ansr.pre>=ans.sub)
    {
        ans.sub=ansl.suf+ansr.pre;
        ans.subl=ansl.sufl;
        ans.subr=ansr.prer;
    }
    if(ansl.sub>=ans.sub)
    {
        ans.sub=ansl.sub;
        ans.subl=ansl.subl;
        ans.subr=ansl.subr;
    }

    if(ansl.pre>=ansl.sum+ansr.pre)
    {
        ans.pre=ansl.pre;
        ans.prer=ansl.prer;
    }
    else
    {
        ans.pre=ansl.sum+ansr.pre;
        ans.prer=ansr.prer;
    }

    if(ansr.suf>ansr.sum+ansl.suf)
    {
        ans.suf=ansr.suf;
        ans.sufl=ansr.sufl;
    }
    else
    {
        ans.suf=ansr.sum+ansl.suf;
        ans.sufl=ansl.sufl;
    }

    return ans;
}

ll kase;

int main()
{
    while(scanf("%lld %lld",&n,&m)==2)
    {
        for(ll i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        printf("Case %lld:\n",++kase);
        BUILD(1,n,1);
        ll l,r;
        while(m--)
        {
            scanf("%lld %lld",&l,&r);
            Node ans=Q(l,r,1);
            printf("%lld %lld\n",ans.subl,ans.subr);
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值