zoukankan      html  css  js  c++  java
  • UVALive 3938

    题目链接: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:
    ",++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
    ",ans.sub_l,ans.sub_r);
    105         }
    106     }
    107 }

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

  • 相关阅读:
    DataTable
    asp.net2.0异步页面和异步web部件
    Ref 和Out 区别
    关于String str =new String("abc")和 String str = "abc"的比较
    MongoDB介绍及安装
    通过MongoDB的samus驱动实现基本数据操作
    SQL 联合索引 与 单一列的索引 比较
    C#字符串处理(String与StringBuilder)
    C#操作文件夹及文件的方法的使用
    常见的sql语句 注意点及用法【区分mysql 和Sqlserver】
  • 原文地址:https://www.cnblogs.com/dilthey/p/7492272.html
Copyright © 2011-2022 走看看