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

     给出一个有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与树状数组是多么的简洁啊!!!









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值