您的位置:首页 > 理论基础 > 数据结构算法

线段树(1):点修改 (uva 1400 Ray,Pass me the Dishes)

2014-04-12 21:50 239 查看
     给出一个有n个元素的数组A1,A2,.........,,An,线段树是一个支持以下两种操作的数据结构:

          Uadate(x,v):吧Ax修改为v.

          Query (L,R):指定区间内特征的查找.

     所有RMQ与树状数组能解决的问题线段树都能解决,线段数结构如下图:

                 



     假设根节点是一个长度为2的h次方的区间,第i层就有2的i次方个节点,每个节点对应一个长度为2的(h-i)次方的区间。最大层编号为h,总结点个数为2的(h+1)次方减1。

     在不同题目中,线段可以有不同的含义,如数轴上的一条线段,或序列中的连续子序列,但是最重要的还是线段上附加的信息以及对附加值的维护,这些信息可以是区间的最小值或最大值,区间内数值的和等等。

     如何实现Uadate(x,v)与 Query (L,R)呢?

给出以下代码:

寻找区间【ql,qr】中的最小值:

int ql,qr;

int query(int o,int L,int R){

  int M=L+(R-L)/2,ans=INF;

  if (ql<=L&&R<=qr) return minv[o];

  if (ql<=M) ans=min(ans,query(o*2,L,M));

  if (M<qr) ans=min(ans,query(o*2+1,M+1,R));

  return ans;

}

修改A【p】=v;

int p,v;

int update(int o,int L,int R){

  int M=L+(R-L)/2;

  if (L==R) minv[o]=v;

  else {

    if (p<=M) update(o*2,L,M);

    else update(o*2+1,M+1,R);

    minv[o]=min(minv[o*2],minv[o*2+1]);

  }

  return 0;

}


      其实说实话,我觉得线段数没有什么固定的模板,因为对于不同的题目,所需要维护的信息各不相同,性质也各不相同,导致的结果就是我们维护区间的方法,查询的方法也就各不相同。

      但是基本思想还是一样的,改变的只是维护方法与查询方法。

    uva  1400 动态最大连续和(Ray,Pass me the Dishes) (很恶心的一道题,做了好久各种bug)

    构造一颗线段树,维护3个值,最大连续和max_s,最大前缀和max_f,最大后缀和max_l.。

    max_s(a,b)代表a<=x<=y<=b且D(x)+........+D(y)最大的二元组(x,y);max_f代表的是a<=x<=b且D(a)+D(a+1)+.......D(x)最大的整数x, max_l代表的是a<=x<=b且D(x)+....D(b)最大的整数x。

    比如,n=8,询问区间(3,5),(3,5)被线段树分成了(3,4),(5,5)。

    对于max_s 有3种情况:

         起点与终点都在(3,4)中,max_s(3,5)=max_s(3,4);

         起点与终点都在(5,5)中,max_s(3,5)=max_s(5,5);

         起点在(3,4),终点在(5,5),max_s(3,5)=(max_l(3,4),max_f(5,5));

   对于max_l 有两种情况:

        x在(3,4)中,max_l(3,5)=max_l(3,4);

        x在(5,5)中,max_l(3,5)=max_l(5,5);

   对于max_f,也有类似的递推。(判断条件自己想,不是我懒>_>)

我的代码:

#include <cstdio>

#include <algorithm>

const int maxn=500005;

using namespace std;

long long sum[maxn*2],m_f[maxn*2],m_l[maxn*2],m_s[maxn*2];

int maxsub[maxn*2][2],maxf[maxn*2],maxl[maxn*2],a[maxn];

int n;

int build(int o,int L,int R){

  int M=L+(R-L)/2;

  if (L==R){

    m_s[o]=m_f[o]=m_l[o]=sum[o]=a[L];

    maxsub[o][0]=maxsub[o][1]=L;

    maxf[o]=maxl[o]=L;

  }

  else {

    build(o*2,L,M);

    build(o*2+1,M+1,R);

    sum[o]=sum[o*2]+sum[o*2+1];

    if (m_f[o*2]<sum[o*2]+m_f[o*2+1]){

      m_f[o]=sum[o*2]+m_f[o*2+1];

      maxf[o]=maxf[o*2+1];

    }

    else {

      m_f[o]=m_f[o*2];

      maxf[o]=maxf[o*2];

    }

    if (m_l[o*2]+sum[o*2+1]>=m_l[o*2+1]){

      m_l[o]=m_l[o*2]+sum[o*2+1];

      maxl[o]=maxl[o*2];

    }

    else {

      m_l[o]=m_l[o*2+1];

      maxl[o]=maxl[o*2+1];

    }

    if (m_s[o*2]<m_s[o*2+1]){

      m_s[o]=m_s[o*2+1];

      maxsub[o][0]=maxsub[o*2+1][0];

      maxsub[o][1]=maxsub[o*2+1][1];

    }

    else {

      m_s[o]=m_s[o*2];

      maxsub[o][0]=maxsub[o*2][0];

      maxsub[o][1]=maxsub[o*2][1];

    }

    if (m_s[o]<m_l[o*2]+m_f[o*2+1]){

      m_s[o]=m_l[o*2]+m_f[o*2+1];

      maxsub[o][0]=maxl[o*2];

      maxsub[o][1]=maxf[o*2+1];

    }

    else if (m_s[o]==m_l[o*2]+m_f[o*2+1]){

      maxsub[o][0]=min(maxsub[o][0],maxl[o*2]);

      maxsub[o][1]=min(maxsub[o][1],maxf[o*2+1]);

    }

  }

}

int gets(int o,int L,int R,int ql,int qr,int &a,int &b,long long &e,int &p1,long long &p2){

  int M=L+(R-L)/2;

  if (ql<=L&&R<=qr) {

     if (e==-1000000000) {

        a=maxsub[o][0];

        b=maxsub[o][1];

        e=m_s[o];

        p1=maxl[o];

        p2=m_l[o];

     }

     else {

     if (m_s[o]>e&&m_s[o]>p2+m_f[o]){

        a=maxsub[o][0];

        b=maxsub[o][1];

        e=m_s[o];

     }

     else if (p2+m_f[o]>=m_s[o]&&p2+m_f[o]>e){

       a=p1;

       b=maxf[o];

       e=p2+m_f[o];

     }

     if (p2+sum[o]<m_l[o]) {

         p1=maxl[o];

         p2=m_l[o];

       }

       else p2=p2+sum[o];

     }

  }

  else {

  if (ql<=M) gets(o*2,L,M,ql,qr,a,b,e,p1,p2);

  if (M<qr)  gets(o*2+1,M+1,R,ql,qr,a,b,e,p1,p2);}

  return 0;

}

int main (){

  int kase=0,q;

  while (scanf("%d%d",&n,&q)!=EOF){

     for (int i=1;i<=n;i++) scanf("%d",&a[i]);

     build(1,1,n);

     printf("Case %d:\n",++kase);

     for (int i=0;i<q;i++){

       int a,b,c,d,p1;scanf("%d%d",&a,&b);c=d=0;

       long long e,p2;

       e=-1000000000;

       gets(1,1,n,a,b,c,d,e,p1,p2);

       printf("%d %d\n",c,d);

     }

  }

  return 0;

}


线段树功能很强大,但是很不好写了。相比起来,RMQ与树状数组是多么的简洁啊!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构