P4565 [CTSC2018]暴力写挂(边分树)

P4565 [CTSC2018]暴力写挂(边分树)

题目大意

给定两棵树,边有权,求下式的最大值

\[\mathrm{depth}(x) + \mathrm{depth}(y) - ({\mathrm{depth}(\mathrm{LCA}(x,y))}+{\mathrm{depth'}(\mathrm{LCA'}(x,y))}) \]

数据范围

对于所有数据, \(n \le 366666 , |v| \le 2017011328\) 。 详细数据范围见下表,表格中的“无” 表示无特殊限制。

测试点编号 \(n \le\) v T 是一条链 T' 是一条链
1 36 =1
2 366 =1
3 1388 >0
4 1999 >0
5 2666 >0
6 5666
7 8666
8 11111
9 12345
10 366666 >0
11 366666
12~13 366666 >0
14 366666
15~16 366666 >0
17 366666
18~20 366666

\(depth(p)\) 和 $depth'(p) $分别表示树 \(T\)\(T'\) 中点 \(1\) 到点 \(p\) 的距离,这里规定,距离指的是经过的边的边权总和,其中 \(\mathrm{depth}(1) = 0\)
\(LCA(x, y)\)\(LCA'(x, y)\) 分别表示树 \(T\)\(T'\) 中点 \(x\) 与点 \(y\) 的最近公共祖先,即在从 \(x\)\(y\) 的最短路径上的距离根经过边数最少的点

解题思路

边分树合并模板题,主要是一些细节

这个式子我们可以化为(其实不化也可做)

\[Ans = \frac 12 \max(dis(x, y)+dep(x)+dep(y)-2*dep'(lca'(x,y))) \]

考虑在第二棵树上枚举 lca,同时边分治合并统计前半部分的最大值,主要还是一些细节和实现方式

可以考虑使用 namespace 肆意使用重复的变量名

代码

const int N = 400250;
struct Tree {
	int ls, rs;
	ll vl, vr;
	Tree() { vl = vr = -1e15; }
	#define ls(p) tree[p].ls
	#define rs(p) tree[p].rs
	#define vl(p) tree[p].vl
	#define vr(p) tree[p].vr
}tree[N<<5];

int rt[N], las[N], nodecnt;
namespace Conquer {
	const int N = 1500005;
	int h[N], ne[N<<1], to[N<<1]; ll w[N<<1], dis[N], tot = 1, cnt;
	inline void add(int x, int y, ll z) {
		ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
	}
	inline void adde(int x, int y, ll z) { add(x, y, z), add(y, x, z); }
	void get_dis(int x, int fa) {
		for (int i = h[x], y; i; i = ne[i])
			if ((y = to[i]) != fa)
				dis[y] = dis[x] + w[i], get_dis(y, x);
	}
	
	int vis[N], siz[N], Siz, lim, ed, n;
	void get(int x, int fa) {
		siz[x] = 1;
		for (int i = h[x], y; i; i = ne[i]) {
			if ((y = to[i]) == fa || vis[i]) continue;
			get(y, x), siz[x] += siz[y];
			int tp = max(siz[y], Siz - siz[y]);
			if (tp < lim) lim = tp, ed = i;
		}
	}
	void dfs(int x, int fa, ll Dis, bool tp) {
		if (x <= n) {
			++nodecnt;
			if (ls(las[x]) == -1) ls(las[x]) = nodecnt;
			else rs(las[x]) = nodecnt;
			if (!tp) ls(nodecnt) = -1, vl(nodecnt) = Dis + dis[x];
			else rs(nodecnt) = -1, vr(nodecnt) = Dis + dis[x];
			las[x] = nodecnt;
		}
		for (int i = h[x], y; i; i = ne[i]) 
			if ((y = to[i]) != fa && !vis[i]) 
				dfs(y, x, Dis + w[i], tp);
	}
	void conquer(int x, int S) {
		if (S <= 1) return; Siz = lim = S, get(x, 0);
		vis[ed] = vis[ed^1] = 1; int tx = to[ed], ty = to[ed^1];
		dfs(tx, 0, 0, 0), dfs(ty, 0, w[ed], 1);
		conquer(ty, S - siz[tx]), conquer(tx, siz[tx]);
	}
	
	void main() {
		for (int i = 1;i <= n; i++)
			rt[i] = las[i] = ++nodecnt, ls(rt[i]) = -1;
		get_dis(1, 0), conquer(1, cnt);
	}
}

namespace T2 {
	int h[N], ne[N<<1], to[N<<1]; ll w[N<<1], tot;
	inline void add(int x, int y, ll z) {
		ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
	}
	ll ans, now;
	int merge(int x, int y) {
		if (!x || !y) return x | y;
		Mx(ans, max(vl(x) + vr(y), vr(x) + vl(y)) - now);
		ls(x) = merge(ls(x), ls(y));
		rs(x) = merge(rs(x), rs(y));
		Mx(vl(x), vl(y)), Mx(vr(x), vr(y));
		return x;
	}
	void dfs(int x, int fa, ll dis) {
		for (int i = h[x]; i; i = ne[i]) {
			int y = to[i]; if (y == fa) continue;
			dfs(y, x, dis + w[i]), now = 2 * dis;
			rt[x] = merge(rt[x], rt[y]);
		}
		Mx(ans, (Conquer::dis[x] - dis) * 2);
	}
	void main(void) {
		dfs(1, 0, 0), write(ans / 2);
	}
}

ll h[N], ne[N<<1], to[N<<1]; ll w[N<<1], tot;
inline void add(int x, int y, ll z) {
	ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
}

int Las[N], cnt;
void dfs(int x, int fa) {
	for (int i = h[x], y; i; i = ne[i]) {
		if ((y = to[i]) == fa) continue; dfs(y, x);
		if (!Las[x]) Conquer::adde(x, y, w[i]), Las[x] = x;
		else {
			Conquer::adde(Las[x], ++cnt, 0);
			Conquer::adde(Las[x] = cnt, y, w[i]);
		}
	}
}

signed main() {
	int n; read(n), cnt = n;
	for (int i = 1, x, y, z;i < n; i++)
		read(x), read(y), read(z), add(x, y, z), add(y, x, z);
	for (int i = 1, x, y, z;i < n; i++)
		read(x), read(y), read(z), T2::add(x, y, z), T2::add(y, x, z);
	dfs(1, 0), Conquer::cnt = cnt, Conquer::n = n, Conquer::main(), T2::main();
	return 0;
}
posted @ 2020-07-23 09:23  Hs-black  阅读(211)  评论(0编辑  收藏  举报