线段树进阶应用学习笔记(二):动态开点线段树相关

动态开点线段树

回忆普通线段树,它的空间一般要开到数组长度的 4(因此经常MLE),考虑如何优化它。

权值线段树

线段树合并

算法流程

复杂度分析

例题二

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 9, LOGN = 22;
struct Edge{
	int v, nex;
} e[N << 1];
int head[N], ecnt;
void addEdge(int u, int v){
	e[++ecnt] = Edge{v, head[u]};
	head[u] = ecnt;
}
struct Node{
	int ls, rs, sum, res;
} t[50 * N];
int ans[N], cnt;
void update(int x){
	if(t[t[x].ls].sum < t[t[x].rs].sum) {
    	t[x].res = t[t[x].rs].res;
    	t[x].sum = t[t[x].rs].sum;
  	} else {
    	t[x].res = t[t[x].ls].res;
    	t[x].sum = t[t[x].ls].sum;
  	}
}
int modify(int a, int b, int l, int r) {
  	if(!a) 
	  return b;
  	if(!b) 
	  return a;
  	if(l == r) {
    	t[a].sum += t[b].sum;
    	return a;
  	}
  	int mid = (l + r) >> 1;
  	t[a].ls = modify(t[a].ls, t[b].ls, l, mid);
  	t[a].rs = modify(t[a].rs, t[b].rs, mid + 1, r);
  	update(a);
  	return a;
}
int Build(int id, int l, int r, int co, int z) {
  	if(!id)
	  	id = ++cnt;
  	if(l == r) {
		t[id].sum += z;
    	t[id].res = co;
    	return id;
  	}
  	int mid = (l + r) >> 1;
  	if(co <= mid)
    	t[id].ls = Build(t[id].ls, l, mid, co, z);
  	else
    	t[id].rs = Build(t[id].rs, mid + 1, r, co, z);
  	update(id);
  	return id;
}
int fa[N][LOGN], dep[N], rt[N];
void dfs1(int u){
	for(int i = head[u]; i; i = e[i].nex){
		int v = e[i].v;
		if(v != fa[u][0]){
			fa[v][0] = u;
			dep[v] = dep[u] + 1;
			dfs1(v);
		}
	}
}
int query(int x, int y){
	if(dep[x] > dep[y])
		swap(x, y);
	int d = dep[y] - dep[x];
	int t = 0;
	while(d){
		if(d & 1)
			y = fa[y][t];
		++t;
		d >>= 1;
	}
	t = 0;
	while(fa[x][t] != fa[y][t])
		++t;
	--t;
	while(t >= 0){
		if(fa[x][t] != fa[y][t]){
			x = fa[x][t];
			y = fa[y][t];
		}
		--t;
	}
	if(x != y){
		x = fa[x][0];
		y = fa[y][0];
	}
	return x;
}
void dfs2(int u) {
  	for(int i = head[u]; i; i = e[i].nex) {
  		int v = e[i].v;
    	if(v == fa[u][0])
			continue;
    	dfs2(v);
    	rt[u] = modify(rt[u], rt[v], 1, 100000);
  	}
  	ans[u] = t[rt[u]].res;
  	if(t[rt[u]].sum == 0)
	  	ans[u] = 0;
}
int n, m;
int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i < n; i++){
		int u, v;
		scanf("%d%d", &u, &v);
		addEdge(u, v);
		addEdge(v, u);
	}
	dfs1(1);
	for(int j = 1, j2 = 2; j2 <= n; ++j, j2 <<= 1)
		for(int i = 1; i <= n; ++i)
			fa[i][j] = fa[fa[i][j - 1]][j - 1];
	for(int i = 1; i <= m; i++){
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		int lca = query(x, y);
		rt[x] = Build(rt[x], 1, 100000, z, 1);
		rt[y] = Build(rt[y], 1, 100000, z, 1);
		rt[lca] = Build(rt[lca], 1, 100000, z, -1);
		if(fa[lca][0])
			rt[fa[lca][0]] = Build(rt[fa[lca][0]], 1, 100000, z, -1);
	}
	dfs2(1);
	for(int i = 1; i <= n; i++)
		printf("%d\n", ans[i]);
	return 0;
} 

线段树分裂

算法流程

复杂度分析

例题三

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 9;
struct Tree{
	int ls, rs, sum;
} t[N << 5];
int rub[N << 5], rt[N << 2], cnt, tot, n, m, idx = 1;
int New(){
	return cnt ? rub[cnt--] : ++tot;
}
void del(int &p){
  t[p].ls = t[p].rs = t[p].sum = 0;
  rub[++cnt] = p;
  p = 0;
}
int build(int id, int L, int R) {
  	if(!id)
	  	id = New();
  	if(L == R){
  		scanf("%lld", &t[id].sum);
  		return id;
	}	
  	int mid = (L + R) >> 1;
  	t[id].ls = build(t[id].ls, L, mid);
  	t[id].rs = build(t[id].rs, mid + 1, R);
  	t[id].sum = t[t[id].ls].sum + t[t[id].rs].sum;
  	return id;
}
int update(int id, int L, int R, int co, int z) {
  	if(!id)
	  	id = New();
  	if(L == R) {
		t[id].sum += z;
    	return id;
  	}
  	int mid = (L + R) >> 1;
  	if(co <= mid)
    	t[id].ls = update(t[id].ls, L, mid, co, z);
  	else
    	t[id].rs = update(t[id].rs, mid + 1, R, co, z);
  	t[id].sum = t[t[id].ls].sum + t[t[id].rs].sum;
  	return id;
}
int merge(int a, int b, int L, int R) {
  	if(!a) 
	  return b;
  	if(!b) 
	  return a;
  	if(L == R) {
    	t[a].sum += t[b].sum;
    	del(b);
    	return a;
  	}
  	int mid = (L + R) >> 1;
  	t[a].ls = merge(t[a].ls, t[b].ls, L, mid);
  	t[a].rs = merge(t[a].rs, t[b].rs, mid + 1, R);
  	t[a].sum = t[t[a].ls].sum + t[t[a].rs].sum;
  	del(b);
  	return a;
}
void split(int &p, int &q, int L, int R, int qL, int qR){
	if(!p)
		return;
	if(L == qL && R == qR){
		q = p;
		p = 0;
		return;
	}
	if(!q)
		q = New();
	int mid = (L + R) >> 1;
	if(qR <= mid)
		split(t[p].ls, t[q].ls, L, mid, qL, qR);
	else if(qL > mid)
		split(t[p].rs, t[q].rs, mid + 1, R, qL, qR);
	else{
		split(t[p].ls, t[q].ls, L, mid, qL, mid);
		split(t[p].rs, t[q].rs, mid + 1, R, mid + 1, qR);
	}
	t[p].sum = t[t[p].ls].sum + t[t[p].rs].sum;
	t[q].sum = t[t[q].ls].sum + t[t[q].rs].sum;
}
int query(int id, int L, int R, int qL, int qR){
	if(!id)
		return 0;
	if(L == qL && R == qR)
		return t[id].sum;
	int ans = 0;
	int mid = (L + R) >> 1;
	if(qR <= mid)
		ans += query(t[id].ls, L, mid, qL, qR);
	else if(qL > mid)
		ans += query(t[id].rs, mid + 1, R, qL, qR);
	else
		ans += query(t[id].ls, L, mid, qL, mid) + query(t[id].rs, mid + 1, R, mid + 1, qR);
	return ans;
}
int kth(int id, int L, int R, int q){
	if(L == R)
		return L;
	int mid = (L + R) >> 1;
	int l = t[t[id].ls].sum;
	if(q <= l)
		return kth(t[id].ls, L, mid, q);
	else
		return kth(t[id].rs, mid + 1, R, q - l);
}
signed main(){
	scanf("%lld%lld", &n, &m);
	rt[1] = build(rt[1], 1, n);
	while(m--){
		int opt, p, x, y, tt, q, k;
		scanf("%lld", &opt);
		if(opt == 0){
			scanf("%lld%lld%lld", &p, &x, &y);
			split(rt[p], rt[++idx], 1, n, x, y);	
		} else if(opt == 1){
			scanf("%lld%lld", &p, &tt);
			rt[p] = merge(rt[p], rt[tt], 1, n);
		} else if(opt == 2){
			scanf("%lld%lld%lld", &p, &x, &q);
			rt[p] = update(rt[p], 1, n, q, x);
		} else if(opt == 3){
			scanf("%lld%lld%lld", &p, &x, &y);
			printf("%lld\n", query(rt[p], 1, n, x, y));
		} else {
			scanf("%lld%lld", &p, &k);
			if(t[rt[p]].sum < k)
				printf("-1\n");
			else
				printf("%lld\n", kth(rt[p], 1, n, k));
		}
	}
	return 0;
}
posted @   JPGOJCZX  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示