线段树(2):区间修改 (uva 11992 FastMatrixOperations)

来源:互联网 发布:js input 光标 编辑:程序博客网 时间:2024/05/17 02:44

我以前写过线段树可以支持点修改与区间查询,其实线段树还可以做的更多,但是也更巧妙,更不好理解:

        现在我们写个线段数要支持2个功能:

              add(L,R,v):把A(L),A(L+1),.......,A(R)的值全部增加v。

              query(L,R):计算子序列A(L),..........,A(R)的元素和,最小值和最大值。

        其实我们也可以用点修改做,但是想想如果这样做了时间上还有优势了吗?没有了,所以我们要换种方法我们将add当做信息存储在区间节点上,比如对一个1到n的区间,我将区间(1,n/2)的每一个元素的增加v。我们可以将这个信息adds存储在(1,n/2)区间对应的节点上,对应的(1,n/2)区间对应的和信息,最大值,最小值信息都要进行改变。当进行query操作是,要将经过的节点的adds值累加起来得到addv,当遇到全部覆盖的区间时,将区间对应的和,最大值,最小值与我们addv结合求出正确的解。

       可能我说的不太明白,请看下面的代码,应该能加深理解:

//维护节点o,它对应区间[L,R]
int maintain(int o,int L,int R){
  int lc=o*2,rc=o*2+1;
  sumv[o]=minv[o]=maxv[o]=0;
  if (R>L){//考虑左右子树
    sumv[o]=sumv[lc]+sumv[rc];
    minv[o]=min(minv[lc],minv[rc]);
    maxv[o]=max(maxv[lc],maxv[rc]);
  }
  minv[o]+=addv[o];
  maxv[o]+=addv[o];
  sumv[o]+=addv[o]*(R-L+1);
  //考虑add操作
  return 0;
}

//修改区间(y1,y2);
int update(int o,int L,int R){
  int lc=o*2,rc=o*2+1;
  if (y1<=L&&y2>=R){  //递归边界
    add[o]+=v;        //累加边界的add值
  }else {
    int M=L+(R-L)/2;
    if (y1<=M) update(lc,L,M);
    if (y2>M) update(rc,M+1,R);
  }
  maintain(o,L,R);    //递归结束前重新计算本节点的附加信息
  return 0;
}


int _min,_max,_sum;

int query(int o,int L,int R,int add){
   if (y1<=L&&y2>=R){
     _sum+=sumv[o]+add*(R-L+1);
     _min=min(_min,minv[o]+add);
     _max=max(_max,maxv[o]+add);
   }
   else {//递归统计,累加参数add
     int M=L+(R-L)/2;
     if (y1<=M) query(o*2,L,M,add+addv[o]);
     if (y2>M)  query(o*2+1,M+1,R,add+addv[o]);
   }
}

       上面三个函数的功能分别是维护区间,修改区间,和区间查询;

       其实这只是基础的应用,还有更深入的应用,我们想想如果我们再加一个操作,set(L,R,v)将区间内的所有的元素都变为v,我们应该如何写这个线段树呢?

       通过分析我们可以得到,set操作与add操作如果先后顺序不同,造成的结果也是完全不同的,所有我们新增加了一个函数pushdown,将set与add的信息下移。

int pushdown(int o,int L,int R,int q){
  if (sets[o][q]>=0){                                     //sets[o][q] 为负的话 表示没有信息
    sets[o*2][q]=sets[o*2+1][q]=sets[o][q];
    sets[o][q]=-1;adds[o][q]=0;
    adds[o*2][q]=adds[o*2+1][q]=0;
  }
  else {
    if (sets[o*2][q]>=0) sets[o*2][q]+=adds[o][q];
    else  adds[o*2][q]+=adds[o][q];
    if (sets[o*2+1][q]>=0) sets[o*2+1][q]+=adds[o][q];
    else  adds[o*2+1][q]+=adds[o][q];
    adds[o][q]=0;
  }
  return 0;
}

update变为:

int update(int o,int L,int R,int q){
  if (x1<=L&&x2>=R){
    if (sets[o][q]>=0){
      sets[o][q]+=v;
    }
    else adds[o][q]+=v;
  }
  else {
    int M=L+(R-L)/2;
    pushdown(o,L,R,q);
    if (x1<=M) update(o*2,L,M,q);
    else maintain(o*2,L,M,q);
    if (x2>M) update(o*2+1,M+1,R,q);
    else maintain(o*2+1,M+1,R,q);
  }
  maintain(o,L,R,q);
  return 0;
}

新增加一个setv操作:

int setv(int o,int L,int R,int q){
   if (x1<=L&&x2>=R){
      sets[o][q]=v;
      adds[o][q]=0;
   }
   else {
    int M=L+(R-L)/2;
    pushdown(o,L,R,q);
    if (x1<=M) setv(o*2,L,M,q);
    else maintain(o*2,L,M,q);
    if (x2>M) setv(o*2+1,M+1,R,q);
    else maintain(o*2+1,M+1,R,q);
   }
   maintain(o,L,R,q);
   return 0;
}

query操作变为如下

int query(int o,int L,int R,int q,int yyy){
  if (sets[o][q]>=0){
    _sum+=(sets[o][q]+yyy)*(min(R,x2)-max(L,x1)+1);
    if (_max<maxv[o][q]+yyy) _max=maxv[o][q]+yyy;
    if (_min>minv[o][q]+yyy) _min=minv[o][q]+yyy;
  }
  else if (x1<=L&&x2>=R){
    _sum+=sum[o][q]+yyy*(R-L+1);
    if (_max<maxv[o][q]+yyy) _max=maxv[o][q]+yyy;
    if (_min>minv[o][q]+yyy) _min=minv[o][q]+yyy;
  }
  else {
    int M=L+(R-L)/2;
    if (x1<=M)query(o*2,L,M,q,yyy+adds[o][q]);
    if (x2>M)query(o*2+1,M+1,R,q,yyy+adds[o][q]);
  }
  return 0;
}

   这几天在总结线段树,我对线段树做了以下几个分类:

                 第一类 单点修改单个信息——>单点修改多个信息——>单点修改多个信息(信息之间有关系)并且设计到区间合并

                 第二类 区间修改单个信息如add操作set操作——>区间修改多个信息并且不同信息之间有联系

                 第三类 就是一看就是应该是线段树但是不知道给如何建立这棵树(|||-_-)像是我之前做的 FZU 2105。

 来道有挑战性的题吧  uva 11992 我今天的例子就是以它为模板的

我的代码:

#include <cstdio>
#include <algorithm>
const int maxn = 500010;
using namespace std;
int sets[maxn*2][23],adds[maxn*2][23],sum[maxn*2][23],minv[maxn*2][23],maxv[maxn*2][23],_sum,_max,_min;
int x1,x2,v;
int maintain(int o,int L,int R,int q){
  if (sets[o][q]>=0){
      if (R>=L){
        sum[o][q]=sets[o][q]*(R-L+1);
        maxv[o][q]=sets[o][q];
        minv[o][q]=sets[o][q];
        adds[o][q]=0;
      }
  }else {
  sum[o][q]=minv[o][q]=maxv[o][q]=0;
  if (R>L){
    sum[o][q]=sum[o*2][q]+sum[o*2+1][q];
    if (minv[o*2][q]>minv[o*2+1][q]) minv[o][q]=minv[o*2+1][q];
    else minv[o][q]=minv[o*2][q];
    if (maxv[o*2][q]<maxv[o*2+1][q]) maxv[o][q]=maxv[o*2+1][q];
    else maxv[o][q]=maxv[o*2][q];
  }
  minv[o][q]+=adds[o][q];maxv[o][q]+=adds[o][q];sum[o][q]+=adds[o][q]*(R-L+1);
  }
  return 0;
}
int pushdown(int o,int L,int R,int q){
  if (sets[o][q]>=0){
    sets[o*2][q]=sets[o*2+1][q]=sets[o][q];
    sets[o][q]=-1;adds[o][q]=0;
    adds[o*2][q]=adds[o*2+1][q]=0;
  }
  else {
    if (sets[o*2][q]>=0) sets[o*2][q]+=adds[o][q];
    else  adds[o*2][q]+=adds[o][q];
    if (sets[o*2+1][q]>=0) sets[o*2+1][q]+=adds[o][q];
    else  adds[o*2+1][q]+=adds[o][q];
    adds[o][q]=0;
  }
  return 0;
}
int update(int o,int L,int R,int q){
  if (x1<=L&&x2>=R){
    if (sets[o][q]>=0){
      sets[o][q]+=v;
    }
    else adds[o][q]+=v;
  }
  else {
    int M=L+(R-L)/2;
    pushdown(o,L,R,q);
    if (x1<=M) update(o*2,L,M,q);
    else maintain(o*2,L,M,q);
    if (x2>M) update(o*2+1,M+1,R,q);
    else maintain(o*2+1,M+1,R,q);
  }
  maintain(o,L,R,q);
  return 0;
}
int setv(int o,int L,int R,int q){
   if (x1<=L&&x2>=R){
      sets[o][q]=v;
      adds[o][q]=0;
   }
   else {
    int M=L+(R-L)/2;
    pushdown(o,L,R,q);
    if (x1<=M) setv(o*2,L,M,q);
    else maintain(o*2,L,M,q);
    if (x2>M) setv(o*2+1,M+1,R,q);
    else maintain(o*2+1,M+1,R,q);
   }
   maintain(o,L,R,q);
   return 0;
}
int query(int o,int L,int R,int q,int yyy){
  if (sets[o][q]>=0){
    _sum+=(sets[o][q]+yyy)*(min(R,x2)-max(L,x1)+1);
    if (_max<maxv[o][q]+yyy) _max=maxv[o][q]+yyy;
    if (_min>minv[o][q]+yyy) _min=minv[o][q]+yyy;
  }
  else if (x1<=L&&x2>=R){
    _sum+=sum[o][q]+yyy*(R-L+1);
    if (_max<maxv[o][q]+yyy) _max=maxv[o][q]+yyy;
    if (_min>minv[o][q]+yyy) _min=minv[o][q]+yyy;
  }
  else {
    int M=L+(R-L)/2;
    if (x1<=M)query(o*2,L,M,q,yyy+adds[o][q]);
    if (x2>M)query(o*2+1,M+1,R,q,yyy+adds[o][q]);
  }
  return 0;
}
int main (){
  int  r,c,m;
  while (scanf("%d%d%d",&r,&c,&m)!=EOF){
     for (int i=0;i<=c*2;i++)
      for (int j=0;j<=r;j++){
        adds[i][j]=sum[i][j]=0;
        sets[i][j]=-1;
        maxv[i][j]=0;
        minv[i][j]=0;
      }
    for (int i=0;i<m;i++){
      int y1,y2,q;scanf("%d",&q);
      if (q==1){
        scanf("%d%d%d%d%d",&y1,&x1,&y2,&x2,&v);
        for (int j=y1;j<=y2;j++) update(1,1,c,j);
      }
      if (q==2){
        scanf("%d%d%d%d%d",&y1,&x1,&y2,&x2,&v);
        for (int j=y1;j<=y2;j++) setv(1,1,c,j);
      }
      if (q==3){
        scanf("%d%d%d%d",&y1,&x1,&y2,&x2);
        _sum=0;_max=-1000000000;_min=1000000000;
        for (int j=y1;j<=y2;j++) query(1,1,c,j,0);
        printf("%d %d %d\n",_sum,_min,_max);
      }
    }
  }
  return 0;
}

          






0 0
原创粉丝点击