动态开点+线段树合并
先来回顾一下线段树基本操作及应用
1.创建线段树
struct segment_tree{
  int L,R,data,lazy,...
}tree[N<<2];
void build(int k,int x,int y){
  if(x==y){
    ...
    return;
  }
  tree[k].L=x;tree[k].R=y;
  int mid=(x+y)>>1;
  build(k<<1,x,mid);
  build((k<<1)+1,mid+1,y);
  push_up(k);
  return;
}
2.区间修改
void add(int k,int x,int y,int t){
  if(x>tree[k].R||y<tree[k].L) return;
  if(tree[k].L<=x&&tree[k].R>=y){
    tree[k].data+=t*(tree[k].R-tree[k].L+1);
    tree[k].lazy+=t;
    return;
  }
  push_down(k);
  add(k<<1,x,y,t);
  add((k<<1)+1,x,y,t);
  push_up(k);
  return;
}
3.区间查询
int ask(int k,int x,int y){
  if(x>tree[k].R||y<tree[k].L) return 0;
  if(tree[k].L<=x&&tree[k].R>=y) return tree[k].sum;
  push_down(k);
  return ask(k<<1,x,y)+ask((k<<1)+1,x,y);
}
以上便是线段树的基本操作(单点,永久化等不一一列出)
线段树应用广泛,几乎所有区间问题它都可以解决。
动态开点
其实就是在线段树基础上并不先把整棵线段树建出来,而是要用到的时候再新建节点。儿子与父亲的关系不再是2倍关系,所以要专门记录儿子节点编号(lson和rson)。
1.新建节点
int build(){//新建个节点
  tot++;
  tree[tot].lson=tree[tot].rson=0;
  ...
  return tot;//返回节点编号
}
2.单点修改操作
void add(int k,int l,int r,int t,int ad){
  if(l==r){
    tree[k].data+=ad;
    return;
  }
  int mid=(l+r)>>1;
  if(t<=mid){
    if(!tree[k].lson) tree[k].lson=build();
    add(tree[k].lson,l,mid,t,ad);
  }
  else{
    if(!tree[k].rson) tree[k].rson=build();
    add(tree[k].rson,mid+1,r,t,ad);
  }
  push_up(k);
  return;
}
在操作和询问时,多出来的步骤只不过就是子节点如果还没有创建,那么就创建罢了。
线段树合并
目的是将两棵线段树合并。
int merge(int p,int q,int l,int r){//返回值为合并后保留的节点
  if(!p) return q;
  if(!q) return p;
  if(l==r){
    tree[p].data+=tree[q].data;
    return p;
  }
  int mid=(l+r)>>1;
  tree[p].lson=merge(tree[p].lson,tree[q].lson,l,mid);
  tree[p].rson=merge(tree[p].rson,tree[q].rson,l,mid);
  push_up(p);
  return p;
}
                    
                
                
            
        
浙公网安备 33010602011771号