线段树是很基本的一种数据结构,也是一种很强大的数据结构,它的衍生有权值线段树,可持久化线段树(主席树).还有现在要介绍的分裂合并版本的线段树.为了方便,下面的文章都用合并树来代替这一长串的称呼.
这种线段树的一般套路都是维护权值,而不是维护序列.一般来说,合并树的基本操作就以下两种:
add:
void add(int &now,int l,int r,int p,int v){
if(!now) now = ++tot;
tree[now].sum += v;
if(l >= r) return;
int mid = l + r >> 1;
if(p <= mid) add(lson(now),l,mid,p,v);
else add(rson(now),mid+1,r,p,v);
}
merge:
int merge(int x,int y){
if(!x||!y) return x+y;
tree[x].sum += tree[y].sum;
lson(x) = merge(lson(x),lson(y));
rson(x) = merge(rson(x),rson(y));
return x;
}
要注意的是,为了节省空间等原因,我们不再用2×i,2×i+1的方式记录左右儿子,而是新开两个指针变量来记录左右儿子.而且也不在线段树中储存左右区间范围,而是在传递的时候增加两个变量记录区间范围.这里的add操作和主席树的add操作有相似之处,merge操作和fhq_treap有相似之处.如果学过这两种数据结构应该比较好理解这两个代码.没学过看一下估计也理解了.
那有了这两种操作之后怎么去写这类型的题目呢?下面给几道例题大伙感受一下.
Promotion Counting P
我们先把1看做根节点.如果只求ans[1]很好做,我们只要开一个线段树或者树状数组,储存所有的子树中的权值,然后求a[1]+1-maxvalue有多少个数就ok了.
那么要求全部的ans数组怎么操作呢?我们考虑一开始为每一个节点都开一颗合并树.因为是动态开点,所以一棵树的空间复杂度是O(logn)的.然后从叶子节点开始,一层一层往上合并,就能跟求ans[1]一样求出每一个节点的答案.
代码:
#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=(int)b;++i)
#define afir(i,a,b) for(int i=(int)a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define mpr(a,b) make_pair(a,b)
#define lson(i) tree[i].l
#define rson(i) tree[i].r
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
inline void read(int &a){
int x = 0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){
x=x*10+ch-'0';ch=getchar();}
a = x*f;
}
struct Tree{
int l,r,sum;
}tree[N*20];
int tn,n,m,ct,nxt[N],head[N],to[N],w[N],ans[N];
vi all;
void addedge(int x,int y){
nxt[++ct] = head[x];head[x] = ct;to[ct] = y;
}
int getpos(int x){
return lower_bound(ALL(all),x) - all.begin();
}
int tot,root[N];
void add(int &now,int l,int r,int p,int v){
if(!now) now = ++tot;
tree[now].sum += v;
if(l >= r) return;
int mid = l + r >> 1;
if(p <= mid) add(lson(now),l,mid,p,v);
else add(rson(now),mid+1,r,p,v);
}
void pre(int u,int fa){
root[u] = ++tot;
add(root[u],1,tn,getpos(w[u]),1);
for(int i = head[u];i;i = nxt[i]){
int y = to[i];
if(y == fa) continue;
pre(y,u);
}
}
int merge(int x,int y){
if(!x||!y) return x+y;
tree[x].sum += tree[y].sum;
lson(x) = merge(lson(x),lson(y));
rson(x) = merge(rson(x),rson(y));
return x;
}
int query(int &now,int l,int r,int ll,int rr){
if(ll > rr)