線段樹合並


線段樹合並,就是將已有的兩棵線段樹合並為一棵,相同位置的信息整合到一起,通常是權值線段樹
比較裸的,就是將一棵線段樹的每一個位置取出來插入另一棵中
但比較高效的線段樹合並可以參照可並堆的合並方式

線段樹合並的原理十分簡單,具體步驟如下:
對於兩顆樹的節點u和v
①如果u為空,返回v
②如果v為空,返回u
③否則,新建節點t,整合u和v的信息,然后遞歸合並u和v的左右子樹

代碼如下:

int merge(int u,int v){
	if (!u) return v;
	if (!v) return u;
	int t = ++cnt;
	sum[t] = sum[u] + sum[v];
	ls[t] = merge(ls[u],ls[v]);
	rs[t] = merge(rs[u],rs[v]);
	return t;
}

容易發現,這樣合並的復雜度取決於兩棵線段樹重合的部分的大小
每有一個位置權值同樣存在,就要\(O(logn)\)的復雜度
不過,由於權值線段樹中被更新的位置通常很均勻分布,所以合並的兩棵線段樹通常具有很小的相似性

可以用這一道水題入門線段樹合並
BZOJ4756

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 100005,maxm = 10000005,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
int val[maxn],b[maxn],tot = 1;
int fa[maxn],lsn[maxn],rbr[maxn];
int sum[maxm],ls[maxm],rs[maxm],rt[maxn],cnt;
int n,ans[maxn];
int getn(int x){return lower_bound(b + 1,b + 1 + tot,x) - b;}
void modify(int& u,int l,int r,int pos){
	if (!u) u = ++cnt;
	sum[u]++;
	if (l == r) return;
	int mid = l + r >> 1;
	if (mid >= pos) modify(ls[u],l,mid,pos);
	else modify(rs[u],mid + 1,r,pos);
}
int query(int u,int l,int r,int L){
	if (!u) return 0;
	if (l >= L) return sum[u];
	int mid = l + r >> 1;
	if (mid >= L) return query(ls[u],l,mid,L) + query(rs[u],mid + 1,r,L);
	return query(rs[u],mid + 1,r,L);
}
int merge(int u,int v){
	if (!u) return v;
	if (!v) return u;
	int t = ++cnt;
	sum[t] = sum[u] + sum[v];
	ls[t] = merge(ls[u],ls[v]);
	rs[t] = merge(rs[u],rs[v]);
	return t;
}
void dfs(int u){
	for (int k = lsn[u]; k; k = rbr[k]){
		dfs(k);
		rt[u] = merge(rt[u],rt[k]);
	}
	ans[u] = query(rt[u],1,tot,val[u] + 1);
	modify(rt[u],1,tot,val[u]);
}
int main(){
	n = read();
	for (int i = 1; i <= n; i++) b[i] = val[i] = read();
	for (int i = 2; i <= n; i++){
		fa[i] = read();
		rbr[i] = lsn[fa[i]];
		lsn[fa[i]] = i;
	}
	sort(b + 1,b + 1 + n);
	for (int i = 2; i <= n; i++) if (b[i] != b[tot]) b[++tot] = b[i];
	for (int i = 1; i <= n; i++) val[i] = getn(val[i]);
	dfs(1);
	for (int i = 1; i <= n; i++) printf("%d\n",ans[i]);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM