链接
https://vjudge.net/problem/UVA-1400
题解
考虑线段树
我怎样得到一个线段树上某个结点所对应区间的最大连续子段和?
使用分治的思想,可以发现有三种情况:
- 这个子段完全在左儿子中
- 这个子段完全在右儿子中
- 这个子段在左儿子中有一部分,右儿子中还有一部分
前两种好处理,就是相同问题的递归。
第三种如何搞?我可以维护线段树上每个结点强制包含右端点的最大值、强制包含左端点的最大值
解决完这个,似乎离最终的问题近了一些
现在,如何用这些信息合并出答案?
暴力:
我直接从线段树上把对应的
l
o
g
n
logn
logn个区间提取出来,然后
O
(
l
o
g
2
n
)
O(log^2n)
O(log2n)暴力,这个做法貌似复杂度有点高,我自己没试过,但是说不定可以过
分治:
利用线段树这个数据结构
考虑我递归的过程
比如我现在在根结点,我要找
[
l
,
r
]
[l,r]
[l,r]中的最大连续子段和
a
n
s
(
l
,
r
)
ans(l,r)
ans(l,r)有三种可能:
- 这个子段完全在左儿子 ( l , m i d ) (l,mid) (l,mid)中
- 这个子段完全在右儿子 ( m i d + 1 , r ) (mid+1,r) (mid+1,r)中
- 这个子段在左儿子中有一部分,右儿子中还有一部分
前两种就是相同问题的递归
第三种怎么解决?
如果强行使用左子树的“强制包含右端点的最大值”和右子树“强制包含左端点的最大值”,会有问题,因为题目对区间有限制
这个时候就要考虑如何搞出“线段树的某个子树受区间
(
l
,
r
)
(l,r)
(l,r)限制时,强制包含左(右)端点的最大值”
其实仔细想一想就能想出来,我可以利用我所记录的信息合并,具体细节在代码中都有了
这样处理一次查询的时间复杂度是
O
(
l
o
g
n
)
O(logn)
O(logn),但是常数貌似有点大
代码
#include <bits/stdc++.h>
#define maxn 500010
#define linf (1ll<<60)
using namespace std;
typedef long long ll;
ll a[maxn], s[maxn], ndtot;
struct interval
{
ll l, r;
interval(ll l, ll r):l(l),r(r){}
interval(){}
};
bool operator<(interval I1, interval I2)
{
ll sum1(s[I1.r]-s[I1.l-1]), sum2(s[I2.r]-s[I2.l-1]);
if(sum1==sum2)
return make_pair(I1.l,I1.r) > make_pair(I2.l,I2.r);
return sum1 < sum2;
}
interval operator+(interval I1, interval I2)
{
if(I1.r!=I2.l-1)
{
printf("Error!\n");
printf("[%lld,%lld] + [%lld,%lld]\n",I1.l,I1.r,I2.l,I2.r);
}
return interval(I1.l,I2.r);
}
struct segtree
{
ll l, r;
segtree *lch, *rch;
interval lmax, rmax, max, sum;
}pool[maxn<<2];
void pushup(segtree *p)
{
// printf("[%lld,%lld]\n",p->l,p->r);
p->lmax = max(p->lch->lmax , p->lch->sum+p->rch->lmax);
p->rmax = max(p->rch->rmax , p->lch->rmax+p->rch->sum);
p->max = max( max(p->lch->max , p->rch->max) , p->lch->rmax+p->rch->lmax );
p->sum = p->lch->sum+p->rch->sum;
}
void build(segtree *p, ll l, ll r)
{
ll mid=l+r>>1;
p->l=l, p->r=r;
if(l==r)
{
p->lmax = p->rmax = p->max = p->sum = interval(l,l);
return;
}
build(p->lch = pool+ ++ndtot, l, mid);
build(p->rch = pool+ ++ndtot, mid+1,r);
pushup(p);
}
interval q_sum(segtree *p, ll l, ll r)
{
ll mid=p->l+p->r>>1;
interval ans;
if(l<=p->l and r>=p->r)return p->sum;
if(l<=mid)ans=q_sum(p->lch,l,r);
if(r>mid)
{
if(l<=mid)ans=ans+q_sum(p->rch,l,r);
else ans=q_sum(p->rch,l,r);
}
return ans;
}
interval q_lmax(segtree *p, ll l, ll r)
{
ll mid = p->l+p->r>>1;
interval ans;
if(l<=p->l and r>=p->r)return p->lmax;
if(l<=mid)
{
ans=q_lmax(p->lch,l,r);
if(r>mid)ans=max(ans,q_sum(p->lch,l,r)+q_lmax(p->rch,l,r));
}
else
{
ans=q_lmax(p->rch,l,r);
}
return ans;
}
interval q_rmax(segtree *p, ll l, ll r)
{
ll mid=p->l+p->r>>1;
interval ans;
if(l<=p->l and r>=p->r)return p->rmax;
if(r>mid)
{
ans=q_rmax(p->rch,l,r);
if(l<=mid)ans=max(ans,q_rmax(p->lch,l,r)+q_sum(p->rch,l,r));
}
else
{
ans=q_rmax(p->lch,l,r);
}
return ans;
}
interval q_max(segtree *p, ll l, ll r)
{
ll mid=p->l+p->r>>1;
if(l<=p->l and r>=p->r)return p->max;
if(l<=mid and r>mid)return max( max( q_max(p->lch,l,r) , q_max(p->rch,l,r) ) , q_rmax(p->lch,l,r)+q_lmax(p->rch,l,r));
if(r<=mid)return q_max(p->lch,l,r);
if(l>mid)return q_max(p->rch,l,r);
}
ll read(ll x=0)
{
ll c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-48;
return f*x;
}
int main()
{
ll N, M, l, r, i, kase=0;
segtree *root;
while(~scanf("%lld%lld",&N,&M))
{
printf("Case %lld:\n",++kase);
for(i=1;i<=N;i++)a[i]=read();
for(i=1;i<=N;i++)s[i]=s[i-1]+a[i];
build(root = pool+(ndtot=1),1,N);
while(M--)
{
l=read(), r=read();
auto ans=q_max(root,l,r);
printf("%lld %lld\n",ans.l,ans.r);
}
}
return 0;
}