实际上就是利用了线段树结构将区间表示成 logN 个节点的性质,若与某个区间连同一个权值的边,做成连到对应线段树的节点上去

例题 CF786B Legacy
考虑建两个线段树,一个父亲到儿子连单向零边,一个儿子到父亲连单向零边。单点到区间连单向边的含义:单点可以从该区间向下走直到所包含的单点;区间向单点连单向边的含义:该区间包含的单点可以向上走再到该单点。
实现细节:多开一个 1~N 的单点,和两个线段树的底层分别连无向零边,而对单点间的连边和其他一些操作就在这个上面做。另外动态开点,记录 ls 和 rs 。
部分

void build1(int x, int l, int r)
{
	if (l==r) { adde(l, x, 0, ++tote), adde(x, l, 0, ++tote); }
	else {
		int mid = (l + r) >> 1;
		ls[x] = ++totn, adde(x, ls[x], 0, ++tote);
		build1(ls[x], l, mid);
		rs[x] = ++totn, adde(x, rs[x], 0, ++tote);
		build1(rs[x], mid+1, r);
	}
}
void build2(int x, int l, int r)
{
	if (l==r) { adde(l, x, 0, ++tote), adde(x, l, 0, ++tote); }
	else {
		int mid = (l + r) >> 1;
		ls[x] = ++totn, adde(ls[x], x, 0, ++tote);
		build2(ls[x], l, mid);
		rs[x] = ++totn, adde(rs[x], x, 0, ++tote);
		build2(rs[x], mid+1, r);
	}
}
void change1(int x, int l, int r, int _l, int _r, int u, int w)
{
	if (l>=_l&&r<=_r) { adde(u, x, w, ++tote); }
	else {
		int mid = (l + r) >> 1;
		if (mid>=_l) change1(ls[x], l, mid, _l, _r, u, w);
		if (mid< _r) change1(rs[x], mid+1, r, _l, _r, u, w);
	}
}
void change2(int x, int l, int r, int _l, int _r, int u, int w)
{
	if (l>=_l&&r<=_r) { adde(x, u, w, ++tote); }
	else {
		int mid = (l + r) >> 1;
		if (mid>=_l) change2(ls[x], l, mid, _l, _r, u, w);
		if (mid< _r) change2(rs[x], mid+1, r, _l, _r, u, w);
	}
}