UVALive 3938 - "Ray, Pass me the dishes!" - [最大连续子列和+线段树]

题目链接:https://cn.vjudge.net/problem/UVALive-3938

参考刘汝佳书上说的:

题意:

给出一个长度为n的序列, 再给出m个询问, 每个询问是在序列 $[a,b]$ 之间的最大连续和. 要你计算出这个这个区间内最大连续和的区间 $[x,y](a \le x \le y \le b)$;

思路:

1、构造线段树,其中每个结点维护3个值,最大连续子列和 $max\_sub$,最大前缀和 $max\_prefix$,最大后缀和 $max\_suffix$。 

具体来说,

$max\_sub(a,b)$ 是满足 $a \le x \le y \le b$ 且 $D_x+D_{x+1}+…+D_y$ 最大的二元组 $(x,y)$;

$max\_prefix(a,b)$ 是满足 $a \le x \le b$ 且 $D_a+D_{a+1}+…+D_x$ 最大的整数 $x$;

$max\_suffix(a,b)$ 是满足 $a \le x \le b$ 且 $D_x+D_{x+1}+…+D_b$ 最大的整数 $x$;

例如 $n=64$,询问为 $(20,50)$,则线段 $[20,50]$ 在线段树的根结点处被分成了两条线段 $[20,32]$ 和 $[33,50]$。则 $max\_sub(20, 50)$ 的起点和终点有3种情况:

情况一:起点和终点都在 $[20,32]$ 中,则 $max\_sub(20,50)=max\_sub(20,32)$。

情况二:起点和终点都在 $[33,50]$ 中,则 $max\_sub(20,50)=max\_sub(33,50)$。

情况三:起点在 $[20,32]$ 中,终点在 $[33,50]$ 中,则 $max\_sub(20,50)=max\_suffix(20, 32) + max\_prefix(33,50)$。

类似地 $max\_suffix$ 和 $max\_prefix$ 也可以这样递推,建树的时间复杂度为 $O(n)$,单组查询的时间复杂度为 $O(\log n)$。

 

当然,我们实际上做的话,没这么简单:

我们每个线段树节点都维护以下几个值:

max_sub:最大连续子列和
max_pre:最大前缀和
max_suf:最大后缀和
pre_i:最大前缀的结束位置
suf_i:最大后缀的开始位置
sum:区间总和

根据上面刘汝佳书上的三种情况可以写出pushup()函数(前缀和、后缀和的更新也在包含里面),然后后面的build()和query()两个函数都可以用pushup()函数。

更多详细的情况,都在代码里体现了。

 

AC代码:

  1 #include<cstdio>
  2 #include<algorithm>
  3 #define MAXN 500000+5
  4 typedef long long ll;
  5 using namespace std;
  6 struct Node{
  7     int l,r;
  8     ll sum,max_sub,max_pre,max_suf;//区间和,最大连续子列和,最大前缀和,最大后缀和
  9     int sub_l,sub_r,pre_i,suf_i;//最大连续子列和的边界,最大前缀和的边界,最大后缀和的边界
 10 }node[4*MAXN];
 11 int n,m,a[MAXN];
 12 void pushup(Node& root,Node& lchild,Node& rchild)
 13 {
 14     root.sum = lchild.sum + rchild.sum;
 15 
 16     if(lchild.max_pre >= lchild.sum + rchild.max_pre)
 17     {
 18         root.max_pre = lchild.max_pre;
 19         root.pre_i = lchild.pre_i;
 20     }
 21     else
 22     {
 23         root.max_pre = lchild.sum + rchild.max_pre;
 24         root.pre_i = rchild.pre_i;
 25     }
 26 
 27     if(lchild.max_suf + rchild.sum >= rchild.max_suf)
 28     {
 29         root.max_suf = lchild.max_suf + rchild.sum;
 30         root.suf_i = lchild.suf_i;
 31     }
 32     else
 33     {
 34         root.max_suf = rchild.max_suf;
 35         root.suf_i = rchild.suf_i;
 36     }
 37 
 38     root.max_sub = lchild.max_sub;
 39     root.sub_l = lchild.sub_l;
 40     root.sub_r = lchild.sub_r;
 41     if(lchild.max_suf + rchild.max_pre > root.max_sub || (lchild.max_suf + rchild.max_pre == root.max_sub && lchild.suf_i < root.sub_l))
 42     {
 43         root.max_sub = lchild.max_suf + rchild.max_pre;
 44         root.sub_l = lchild.suf_i;
 45         root.sub_r = rchild.pre_i;
 46     }
 47     if(rchild.max_sub > root.max_sub)
 48     {
 49         root.max_sub = rchild.max_sub;
 50         root.sub_l = rchild.sub_l;
 51         root.sub_r = rchild.sub_r;
 52     }
 53 }
 54 void build(int root,int l,int r)
 55 {
 56     node[root].l=l;
 57     node[root].r=r;
 58     if(l==r)
 59     {
 60         node[root].sum=a[l];
 61         node[root].max_sub=a[l]; node[root].sub_l=l; node[root].sub_r=l;
 62         node[root].max_pre=a[l]; node[root].pre_i=l;
 63         node[root].max_suf=a[l]; node[root].suf_i=l;
 64     }
 65     else
 66     {
 67         int mid=l+(r-l)/2;
 68         build(root*2,l,mid);
 69         build(root*2+1,mid+1,r);
 70         pushup(node[root],node[root*2],node[root*2+1]);
 71     }
 72 }
 73 Node query(int root,int st,int ed)
 74 {
 75     if(st==node[root].l && node[root].r==ed) return node[root];
 76 
 77     int mid=(node[root].l+node[root].r)/2;
 78     if(ed<=mid) return query(root*2,st,ed);
 79     else if(st>mid) return query(root*2+1,st,ed);
 80     else
 81     {
 82         Node r1=query(root*2,st,mid);
 83         Node r2=query(root*2+1,mid+1,ed);
 84         Node ans;
 85 
 86         ans.l = st, ans.r=ed;
 87         pushup(ans,r1,r2);
 88 
 89         return ans;
 90     }
 91 }
 92 int main()
 93 {
 94     int kase=0;
 95     while(scanf("%d%d",&n,&m)!=EOF)
 96     {
 97         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
 98         build(1,1,n);
 99         printf("Case %d:\n",++kase);
100         for(int i=1,l,r;i<=m;i++)
101         {
102             scanf("%d%d",&l,&r);
103             Node ans=query(1,l,r);
104             printf("%d %d\n",ans.sub_l,ans.sub_r);
105         }
106     }
107 }

PS.这道题可以说要对普通线段树模板进行巨大的改动,是一道可以加深对线段树理解的好题。

posted @ 2017-09-07 22:32  Dilthey  阅读(340)  评论(0编辑  收藏  举报