动态开点+线段树合并
先来回顾一下线段树基本操作及应用
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号