P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 题解

solution

对于树上的区间加操作,考虑转化为树上差分:对于两个节点 \(x,y\),只需在 \(sum_x+1,sum_y+1,sum_{LCA_{x,y}}-1,sum_{fa(LCA_{x,y})}-1\) 即可,只需一个倍增 LCA(好像不倍增也能过)。对于统计答案,由于线段树合并统计的是一个子树的信息,所以差分后一个子树的信息就是这单个根节点的信息。

code

#include <bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define I inline
#define ll long long
#define CI const int
#define RI register int
#define W while
#define gc getchar
#define D isdigit(c=gc())
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define ms(a,x) memset((a),(x),sizeof(a))
using namespace std;
namespace SlowIO {
	I void readc (char& c) {W (isspace (c = gc ()));}
	Tp I void read (Ty& x) {char c; int f = 1; x = 0; W (! D) f = c ^ '-' ? 1 : -1; W (x = (x * 10) + (c ^ 48), D); x *= f;}
	Ts I void read (Ty& x, Ar&... y) {read (x); read (y...);}
} using namespace SlowIO;
CI LGN = 50, N = 1e5, N2 = 2e5, N4 = 5e6; int n, m, anss[N + 5], MAX = N;
namespace graph {
	int nxt[N2 + 5], to[N2 + 5], hd[N + 5], cnt = 0;
	void add (int u, int v) {++ cnt; to[cnt] = v; nxt[cnt] = hd[u]; hd[u] = cnt;}
} using namespace graph;
namespace FindLCA {
	int lg[N + 5], dep[N + 5], fa[N + 5][LGN + 5];
	void initLCA () {RI i, j; for (i = 1; i <= n; ++ i) lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);}
	void dfsfa (int now, int Fa) {
		RI i, j; fa[now][0] = Fa; dep[now] = dep[Fa] + 1; for (i = 1; i <= lg[dep[now]]; ++ i) fa[now][i] = fa[fa[now][i - 1]][i - 1];
		for (i = hd[now]; i; i = nxt[i]) if (to[i] != Fa) dfsfa (to[i], now);
	}
	int LCA (int x, int y) {
		RI i, j; if (dep[x] < dep[y]) swap (x, y); W (dep[x] > dep[y]) x = fa[x][lg[dep[x] - dep[y]] - 1]; if (x == y) return x;
		for (i = lg[dep[x]] - 1; i >= 0; -- i) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0];
	}
} using namespace FindLCA;
namespace LineTree {
	int ans[N4 + 5], sum[N4 + 5], sl[N4 + 5], sr[N4 + 5], rt[N + 5], ntot = 0;
	void pushup (int root) {
		if (sum[sl[root]] > sum[sr[root]]) sum[root] = sum[sl[root]], ans[root] = ans[sl[root]];
		else if (sum[sr[root]] > sum[sl[root]]) sum[root] = sum[sr[root]], ans[root] = ans[sr[root]];
		else sum[root] = sum[sl[root]], ans[root] = min (ans[sl[root]], ans[sr[root]]);
	}
	void update (int &root, int L, int R, int pos, int k) {
		if (! root) root = ++ ntot; if (L == R) {ans[root] = L; sum[root] += k; return ;} int mid = (L + R) >> 1;
		if (pos <= mid) update (sl[root], L, mid, pos, k); else update (sr[root], mid + 1, R, pos, k); pushup (root);
	}
	int merge (int a, int b, int L, int R) {
		if (! a || ! b) return a + b; if (L == R) {ans[a] = ans[b]; sum[a] += sum[b]; return a;} int mid = (L + R) >> 1;
		sl[a] = merge (sl[a], sl[b], L, mid); sr[a] = merge (sr[a], sr[b], mid + 1, R); pushup (a); return a;
	}
} using namespace LineTree;
void dfs (int now, int Fa) {
	RI i, j; for (i = hd[now]; i; i = nxt[i]) {if (to[i] != Fa) dfs (to[i], now), rt[now] = merge (rt[now], rt[to[i]], 1, MAX);} anss[now] = ans[rt[now]];
}
int main () {
	RI i, j; for (read (n, m), initLCA (), i = 2; i <= n; ++ i) {int x, y; read (x, y); add (x, y); add (y, x);} dfsfa (1, 0); for (i = 1; i <= n; ++ i) rt[i] = i, ++ ntot;
	for (i = 1; i <= m; ++ i) {
		int x, y, z; read (x, y, z); update (rt[x], 1, MAX, z, 1); update (rt[y], 1, MAX, z, 1); int Lca = LCA (x, y); update (rt[Lca], 1, MAX, z, -1);
		if (fa[Lca][0]) update (rt[fa[Lca][0]], 1, MAX, z, -1);
	} dfs (1, 0); for (i = 1; i <= n; ++ i) printf ("%d \n", anss[i]);
	return 0;
}
posted @ 2022-08-21 22:55  ClapEcho233  阅读(24)  评论(0编辑  收藏  举报