就像STL那样:封装的动态开点线段树(用于线段树合并)

Preface

起因是这个万恶的P9067,一个数据结构题,当时才搞了01字典树的板子,想trytry合并的题的,然后就搜到了这道。(虽然最后完全和这个没有关系)。

然后感觉用线段树合并做就可以了,于是抄了个之前封装的一个板子,但是一点都不好用(sad)。空间方面又是头疼,感觉封装了又好像没有封装,对比一下用map写的AC代码,感觉差的不是一点,而且本人实在是热衷搞板子,于是打算搞一个封装好的动态开点的线段树,可以像STL那样使用非常灵便。

code

先上代码:


//使用之前要先使用clear函数
namespace SegTree {

	constexpr int MAXN = (N * 40);

	struct info {
		int mmax = 0;
		int cnt = 0;

		void apply(int pos,int k) {
			mmax += k;
			cnt = pos;
		}

		void merge(info &q1) {
			mmax += q1.mmax;
		}

		void clear() {

		}

		friend info operator+(const info &q1, const info &q2) {
			info q;
			q.cnt = 0;
			if (q1.mmax == q2.mmax) {
				q.mmax = q1.mmax;
				q.cnt = min(q1.cnt, q2.cnt);
			}
			else {
				q.mmax = max(q1.mmax, q2.mmax);
				if (q.mmax == q1.mmax) q.cnt = q1.cnt;
				if (q.mmax == q2.mmax) q.cnt = q2.cnt;
			}
			return q;
		}


	} F[MAXN];

	bool lf[MAXN];
	int son[MAXN][2];
	stack<int> st;
	int tot;


	void clear() {
		tot = 0;
		lf[0] = 0;
		while (!st.empty()) st.pop();
	}

	int newNode() {
		int tem;
		if (!st.empty()) {
			tem = st.top();
			st.pop();
		}
		else
			tem = ++tot;
		F[tem] = info(), lf[tem] = 1, lf[son[tem][0]] = lf[son[tem][1]] = 0;
		son[tem][0] = son[tem][1] = 0;
		return tem;
	}

	void del(int x) { lf[x] = 0, F[x].clear(), st.push(x); }

	class Seg {
#define xl son[x][0]
#define xr son[x][1]
		int L, R;
		int rt = 1;

		void add(int &x, int l, int r, int l1, int r1, int k) {
			if (l1 > r1) return;
			if (!lf[x]) x = newNode();
			if (l1 <= l and r <= r1) {
				F[x].apply(l1, k);
				return;
			}
			int mid = l + r >> 1;
			if (r1 <= mid) add(xl, l, mid, l1, r1, k);
			else if (mid < l1) add(xr, mid + 1, r, l1, r1, k);
			else add(xl, l, mid, l1, mid, k), add(xr, mid + 1, r, mid + 1, r1, k);
			F[x] = F[xl] + F[xr];
		}

		info qry(int x, int l, int r, int l1, int r1) {
			if (l1 > r1 or !lf[x]) return info();
			if (l1 <= l and r <= r1) return F[x];
			int mid = l + r >> 1;
			if (r1 <= mid) return qry(xl, l, mid, l1, r1);
			else if (mid < l1) return qry(xr, mid + 1, r, l1, r1);
			else { return qry(xl, l, mid, l1, mid) + qry(xr, mid + 1, r, mid + 1, r1); }
		}

		int merge(int x,int y,int l,int r) {
			if (!x or !y) return x + y;
			if (l == r) {
				F[x].merge(F[y]);
				del(y);
				return x;
			}
			int mid = l + r >> 1;
			son[x][0] = merge(son[x][0], son[y][0], l, mid);
			son[x][1] = merge(son[x][1], son[y][1], mid + 1, r);
			F[x] = F[xl] + F[xr];
			del(y);
			return x;
		}

#undef xl
#undef xr

	public:
		void clear(int l, int r) {
			L = l, R = r;
			rt = newNode();
		}

		void add(int pos, int k) { add(rt, L, R, pos, pos, k); }
		info qry(int l, int r) { return qry(rt, L, R, l, r); }
		void merge(Seg &y) { rt = merge(rt, y.rt, L, R); }
	};

}

细节部分:

info (节点)结构体:

有一个大的命名空间包着,其中我们在使用的时候根据每个题的变动,修改的地方就是namespace里的info

里面有applyclearmergeup函数,以下是注释:

apply:用来实现单点添加。

clear:例如此题中链表需要clear掉,不然会占空间,所以用来clear一些节点维护的数据结构。(一般也可以不clear,直接空着放那就好了)

merge:用来完善线段树合并中节点合并时具体合并哪些信息,也是我们经常需要修改的地方。

up:是一个重载运算符,如字面意思就是一个实现合并操作的。

SegTree里的变量:

lf数组代表当前这个点是否存活,1代表或者,0代表不在。

st是一个栈,用来实现线段树合并里的垃圾回收操作(这一步也可以不用,如果节点过多数组开不下,就可以用这个,否则也可以不用)

tot:用来存节点总数的,和st一起实现newNode函数。

son数组:字面意思,存当前节点的左右儿子的编号

SegTree里的函数:

clear函数:用来清空的,面对多组数据使用,以及使用这个封装的线段树之前要clear下。

newNode:用来建立一个新的节点

del:用来将当前节点删除(做垃圾回收的)

Seg class:

这个就是线段树,其中每一个线段树都有一个自己的根(编号,也是里面的变量rt),自己所维护的范围( L~R区间 )。

然后里面有三个操作,添加,求和,合并。这里就不细说内容,学过线段树合并的就大概都知道了。

使用注意事项:

其中主要需要修改的就是info,其他的都当做黑盒来处理就可以。

使用方面各位可以参考一下,STL中别的容器平常是怎么使用的,就怎么使用这个。

但是不同的地方就是声明变量后都一定要clear,线段树的clear函数有两个参数lr,用来设定当前线段树的维护的区间。

以及在使用这个封装的之前要先使用空间里的clear函数。

直接声明SegTree::Seg  tem,那么tem就是一个线段树,调用函数就是tem.add(pos,val),就是在这个线段树的pos位置加上val,其他的函数也都是。如果要合并两颗线段树等操作就看如下代码:

signed main() {
	fileRead();
	kuaidu();
	T = 1;
	//cin >> T;
	while (T--) {
		SegTree::clear();
		SegTree::Seg seg[3];
		rep(i, 0, 2) seg[i].clear(1, 10000);
		seg[1].add(12, 1);
		seg[1].qry(12, 142);
		seg[1].merge(seg[2]);

	}
	return 0;
}

雨天的尾巴(线段树合并板子)

下面是具体的使用:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
#include <queue>
#include <map>
#include <unordered_map>
#include <iomanip>
#include <stack>
#define endl '\n'
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rep2(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;

template<typename T>
void cc(const vector<T> &tem) {
	for (const auto &x: tem) cout << x << ' ';
	cout << endl;
}

template<typename T>
void cc(const T &a) { cout << a << endl; }

template<typename T1, typename T2>
void cc(const T1 &a, const T2 &b) { cout << a << ' ' << b << endl; }

template<typename T1, typename T2, typename T3>
void cc(const T1 &a, const T2 &b, const T3 &c) { cout << a << ' ' << b << ' ' << c << endl; }

void fileRead() {
#ifdef LOCALL
	freopen("D:\\AADVISE\\Clioncode\\untitled2\\in.txt", "r", stdin);
	freopen("D:\\AADVISE\\Clioncode\\untitled2\\out.txt", "w", stdout);
#endif
}

void kuaidu() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); }

inline int max(int a, int b) {
	if (a < b) return b;
	return a;
}

inline double max(double a, double b) {
	if (a < b) return b;
	return a;
}

inline int min(int a, int b) {
	if (a < b) return a;
	return b;
}

inline double min(double a, double b) {
	if (a < b) return a;
	return b;
}

void cmax(int &a, const int &b) { if (b > a) a = b; }
void cmin(int &a, const int &b) { if (b < a) a = b; }
void cmin(double &a, const double &b) { if (b < a) a = b; }
void cmax(double &a, const double &b) { if (b > a) a = b; }

template<typename T>
using vec = vector<T>;
using PII = pair<int, int>;
using i128 = __int128;


//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int ans[N];
int dep[N];
int fu[N][33];
bool book[N];
vector<int> A[N];
//--------------------------------------------------------------------------------
//struct or namespace:

//使用之前要先使用clear函数
namespace SegTree {

	constexpr int MAXN = (N * 60);

	struct info {
		int mmax = 0;
		int cnt = 0;

		void apply(int pos,int k) {
			mmax += k;
			cnt = pos;
		}

		void merge(info &q1) {
			mmax += q1.mmax;
		}

		void clear() {

		}

		friend info operator+(const info &q1, const info &q2) {
			info q;
			q.cnt = 0;
			if (q1.mmax == q2.mmax) {
				q.mmax = q1.mmax;
				q.cnt = min(q1.cnt, q2.cnt);
			}
			else {
				q.mmax = max(q1.mmax, q2.mmax);
				if (q.mmax == q1.mmax) q.cnt = q1.cnt;
				if (q.mmax == q2.mmax) q.cnt = q2.cnt;
			}
			return q;
		}


	} F[MAXN];

	bool lf[MAXN];
	int son[MAXN][2];
	stack<int> st;
	int tot;


	void clear() {
		tot = 0;
		lf[0] = 0;
		while (!st.empty()) st.pop();
	}

	int newNode() {
		int tem;
		if (!st.empty()) {
			tem = st.top();
			st.pop();
		}
		else
			tem = ++tot;
		F[tem] = info(), lf[tem] = 1, lf[son[tem][0]] = lf[son[tem][1]] = 0;
		son[tem][0] = son[tem][1] = 0;
		return tem;
	}

	void del(int x) { lf[x] = 0, F[x].clear(), st.push(x); }

	class Seg {
#define xl son[x][0]
#define xr son[x][1]
		int L, R;
		int rt = 1;

		void up(int x) {
			if (lf[xl] and lf[xr]) F[x] = F[xl] + F[xr];
			else if (lf[xl]) F[x] = F[xl];
			else if (lf[xr]) F[x] = F[xr];
		}

		void add(int &x, int l, int r, int l1, int r1, int k) {
			if (l1 > r1) return;
			if (!lf[x]) x = newNode();
			if (l1 <= l and r <= r1) {
				F[x].apply(l1, k);
				return;
			}
			int mid = l + r >> 1;
			if (r1 <= mid) add(xl, l, mid, l1, r1, k);
			else if (mid < l1) add(xr, mid + 1, r, l1, r1, k);
			else add(xl, l, mid, l1, mid, k), add(xr, mid + 1, r, mid + 1, r1, k);
			// up(x);
			F[x] = F[xl] + F[xr];
		}

		info qry(int x, int l, int r, int l1, int r1) {
			if (l1 > r1 or !lf[x]) return info();
			if (l1 <= l and r <= r1) return F[x];
			int mid = l + r >> 1;
			if (r1 <= mid) return qry(xl, l, mid, l1, r1);
			else if (mid < l1) return qry(xr, mid + 1, r, l1, r1);
			else { return qry(xl, l, mid, l1, mid) + qry(xr, mid + 1, r, mid + 1, r1); }
		}

		int merge(int x,int y,int l,int r) {
			if (!x or !y) return x + y;
			if (l == r) {
				F[x].merge(F[y]);
				del(y);
				return x;
			}
			int mid = l + r >> 1;
			son[x][0] = merge(son[x][0], son[y][0], l, mid);
			son[x][1] = merge(son[x][1], son[y][1], mid + 1, r);
			// up(x);
			F[x] = F[xl] + F[xr];
			del(y);
			return x;
		}

#undef xl
#undef xr

	public:
		void clear(int l, int r) {
			L = l, R = r;
			rt = newNode();
		}

		void add(int pos, int k) { add(rt, L, R, pos, pos, k); }
		info qry(int l, int r) { return qry(rt, L, R, l, r); }
		void merge(Seg &y) { rt = merge(rt, y.rt, L, R); }
	};

}

class DSU {
	struct Info {
		int fa;
		int siz;
		SegTree::Seg seg;
	} dsu[N];

public:
	Info &operator()(const int &x) { return dsu[find(x)]; }
	Info &operator[](const int &x) { return dsu[x]; }

	void clear(int n) {
		//TODO 初始化
		rep(i, 0, n) {
			dsu[i].fa = i, dsu[i].siz = 1;
			dsu[i].seg.clear(1, 100000);
		}
	}

	void merge(int x, int y) {
		x = find(x), y = find(y);
		if (x == y) return;
		dsu[y].fa = dsu[x].fa;
		if (dsu[x].siz < dsu[y].siz) swap(dsu[x], dsu[y]);
		dsu[y].fa = dsu[x].fa, dsu[x].siz += dsu[y].siz;
		dsu[x].seg.merge(dsu[y].seg);
		//TODO 合并操作

	}

	int find(int x) { return x == dsu[x].fa ? x : dsu[x].fa = find(dsu[x].fa); }
	bool same(int x, int y) { return (find(x) == find(y)); }
} dsu;

//--------------------------------------------------------------------------------


void dfs(int now, int father) {
	dep[now] = dep[father] + 1;
	fu[now][0] = father;
	for (int i = 1; i <= 20; i++) fu[now][i] = fu[fu[now][i - 1]][i - 1];
	for (auto y: A[now]) {
		if (y == father) continue;
		dfs(y, now);
	}
}

int LCA(int x, int y) {
	if (dep[x] < dep[y]) std::swap(x, y);
	for (int i = 20; i >= 0; --i)
		if (dep[fu[x][i]] >= dep[y]) x = fu[x][i];
	if (x == y) return x;
	for (int i = 20; i >= 0; --i)
		if (fu[x][i] != fu[y][i]) x = fu[x][i], y = fu[y][i];
	return fu[x][0];
}

void dfs2(int x, int pa) {
	for (auto y: A[x]) {
		if (y == pa) continue;
		dfs2(y, x);
		dsu.merge(x, y);
	}
	SegTree::info tem = dsu(x).seg.qry(1, 100000);
	if (tem.mmax == 0) ans[x] = 0;
	else ans[x] = tem.cnt;
}

signed main() {
#ifdef LOCALL
	freopen("D:\\AADVISE\\Clioncode\\untitled2\\in.txt", "r", stdin);
	freopen("D:\\AADVISE\\Clioncode\\untitled2\\out.txt", "w", stdout);
#endif
	kuaidu();
	int dd;

	cin >> dd >> m;
	const int n = dd;


	// cout << n << endl;
	rep(i, 1, n - 1) {
		int a, b;
		cin >> a >> b;
		A[a].push_back(b);
		A[b].push_back(a);
	}

	SegTree::clear();
	dsu.clear(n);

	dfs(1, 0);
	rep(i, 1, m) {
		int a, b, c;
		cin >> a >> b >> c;
		int t = LCA(a, b);
		dsu(a).seg.add(c, 1);
		dsu(b).seg.add(c, 1);
		if (fu[t][0])dsu(fu[t][0]).seg.add(c, -1);
		if (t)dsu(t).seg.add(c, -1);

	}

	dfs2(1, 0);

	rep(i, 1, n) {
		cout << ans[i] << endl;
	}
	return 0;
}


/*


*/

PostScript

板子也许会有问题,如果有请指出来。

posted @   AdviseDY  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示