UOJ#400. 【CTSC2018】暴力写挂

传送门
看到要求两棵树的 \(lca\) 深度不太好操作
考虑枚举第二棵树的 \(lca\),这样剩下的都是只和第一棵树有关的
而注意到 \(dis(x,y)=d(x)+d(y)-2d(lca(x,y))\)
那么 \(d(x)+d(y)-d(lca(x,y))=\frac{1}{2}(dis(x,y)+d(x)+d(y))\)
这样就好多了,可以直接沿用WC2018通道的做法
对于第一棵树进行边分治,把两边的集合合起来在第二棵树上建个虚树
然后在虚树上枚举 \(lca\)\(dp\) 统计不属于同一个集合的答案
复杂度 \(\Theta(nlog^2n)\) 可以强行优化到 \(nlogn\)
上述做法不够优美
考虑另外一种做法,仍然是对于第一棵树进行边分治
对于每个点,维护每一次边分治时的集合和到分治中心的距离
那么只需要在第二棵树上枚举 \(lca\) 统计每次边分治时不同集合的最大的贡献就好了
这个可以用线段树(二叉树)合并实现,\(nlogn\)
然后考虑怎么维护每一次边分治时的集合和到分治中心的距离
直接想法是直接暴力插入,\(nlog^2n\)
然而边分治每次是分成两个集合,类似一个线段树的结构
所以可以对于每个点维护一个线段树(二叉树),要保证树上每个点和是哪一次边分治对应
只需要将这些点的树的当前点记录下来
每次边分治的第一个集合向左跳,第二个向右跳就可以 \(nlogn\) 建树了
复杂度 \(\Theta(nlogn)\)

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

namespace IO {
	const int maxn(1 << 21 | 1);

	char ibuf[maxn], *iS, *iT, c;
	int f;
	
	inline char Getc() {
		return iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++)) : *iS++;
	}

	template <class Int> inline void In(Int &x) {
		for (c = Getc(), f = 1; c < '0' || c > '9'; c = Getc()) f = c == '-' ? -1 : 1;
		for (x = 0; c >= '0' && c <= '9'; c = Getc()) x = (x << 1) + (x << 3) + (c ^ 48);
		x *= f;
	}
}

using IO :: In;

const int maxn(4e5 + 5);
const ll inf(1e18);

int first1[maxn << 1], first2[maxn], cnt, head[maxn], m, n, fw[maxn << 1], tmp[maxn << 1], len;
int size[maxn << 1], mn, rte, vis[maxn << 2], sz;
int rt[maxn], tot, ls[maxn * 40], rs[maxn * 40], fa[maxn], fc[maxn];
ll dis[maxn], ans, lmx[maxn * 40], rmx[maxn * 40];

struct Edge {
	int to, next, w;
} edg[maxn << 1], edge1[maxn << 2], edge2[maxn << 1];

inline void Addpre(int u, int v, int w) {
	edg[cnt] = (Edge){v, head[u], w}, head[u] = cnt++;
	edg[cnt] = (Edge){u, head[v], w}, head[v] = cnt++;
}

inline void Add1(int u, int v, int w) {
	edge1[cnt] = (Edge){v, first1[u], w}, first1[u] = cnt++;
	edge1[cnt] = (Edge){u, first1[v], w}, first1[v] = cnt++;
}

inline void Add2(int u, int v, int w) {
	edge2[cnt] = (Edge){v, first2[u], w}, first2[u] = cnt++;
	edge2[cnt] = (Edge){u, first2[v], w}, first2[v] = cnt++;
}

int Build(int l, int r) {
	if (l > r) return 0;
	if (l == r) return tmp[l];
	int cur, ls, rs, mid;
	cur = ++m, mid = (l + r) >> 1;
	ls = Build(l, mid), rs = Build(mid + 1, r);
	if (ls) Add1(cur, ls, fw[ls]);
	if (rs) Add1(cur, rs, fw[rs]);
	return cur;
}

void Rebuild(int u, int ff) {
	int e, v, ls, rs, mid;
	len = 0;
	for (e = head[u]; ~e; e = edg[e].next)
		if ((v = edg[e].to) != ff) fw[v] = edg[e].w, tmp[++len] = v;
	mid = (len + 1) >> 1;
	ls = Build(1, mid), rs = Build(mid + 1, len);
	if (ls) Add1(u, ls, fw[ls]);
	if (rs) Add1(u, rs, fw[rs]);
	for (e = head[u]; ~e; e = edg[e].next)
		if ((v = edg[e].to) != ff) dis[v] = dis[u] + fw[v], Rebuild(v, u);
}

void Getroot(int u, int ff, int fe) {
	int e, v;
	size[u] = 1;
	for (e = first1[u]; ~e; e = edge1[e].next)
		if ((v = edge1[e].to) != ff && !vis[e]) {
			Getroot(v, u, e);
			size[u] += size[v];
		}
	if (abs(sz - size[u] * 2) < mn) mn = abs(sz - size[u] * 2), rte = fe;
}

void Dfs1(int u, int ff, int type, ll d) {
	if (u <= n) {
		++tot, lmx[tot] = rmx[tot] = -inf;
		if (fa[u]) fc[u] ? rs[fa[u]] = tot : ls[fa[u]] = tot;
		else rt[u] = tot;
		fa[u] = tot, fc[u] = type;
		type ? rmx[tot] = d + dis[u] : lmx[tot] = d + dis[u];
	}
	int e, v;
	for (e = first1[u]; ~e; e = edge1[e].next)
		if ((v = edge1[e].to) != ff && !vis[e]) Dfs1(v, u, type, d + edge1[e].w);
}

void Solve(int nrt) {
	if (nrt == -1 || vis[nrt]) return;
	int rt1, rt2, tmp1, tmp2;
	rt1 = edge1[nrt].to, rt2 = edge1[nrt ^ 1].to;
	vis[nrt] = vis[nrt ^ 1] = 1;
	tmp1 = size[rt1] > size[rt2] ? sz - size[rt2] : size[rt1];
	tmp2 = size[rt2] > size[rt1] ? sz - size[rt1] : size[rt2];
	Dfs1(rt1, rt2, 0, edge1[nrt].w), Dfs1(rt2, rt1, 1, 0);
	sz = tmp1, mn = m + 1, Getroot(rt1, rt2, -1), Solve(rte);
	sz = tmp2, mn = m + 1, Getroot(rt2, rt1, -1), Solve(rte);
}

int Merge(int x, int y, ll del) {
	if (!x || !y) return x | y;
	ans = max(ans, lmx[x] + rmx[y] - del);
	ans = max(ans, lmx[y] + rmx[x] - del);
	lmx[x] = max(lmx[x], lmx[y]), rmx[x] = max(rmx[x], rmx[y]);
	ls[x] = Merge(ls[x], ls[y], del), rs[x] = Merge(rs[x], rs[y], del);
	return x;
}

void Dfs2(int u, int ff, ll d) {
	int e, v;
	ans = max(ans, dis[u] * 2 - d * 2);
	for (e = first2[u]; ~e; e = edge2[e].next)
		if ((v = edge2[e].to) != ff) {
			Dfs2(v, u, d + edge2[e].w);
			rt[u] = Merge(rt[u], rt[v], d + d);
		}
}

int main() {
	int i, u, v, w;
	memset(head, -1, sizeof(head));
	memset(first1, -1, sizeof(first1));
	memset(first2, -1, sizeof(first2));
	ans = -inf, In(n), m = n;
	for (i = 1; i < n; ++i) In(u), In(v), In(w), Addpre(u, v, w);
	cnt = 0;
	for (i = 1; i < n; ++i) In(u), In(v), In(w), Add2(u, v, w);
	cnt = 0, Rebuild(1, 0);
	sz = m, mn = m + 1, Getroot(1, 0, -1);
	Solve(rte), Dfs2(1, 0, 0);
	printf("%lld\n", ans >> 1);
    return 0;
}
posted @ 2019-01-06 17:56  Cyhlnj  阅读(167)  评论(0编辑  收藏  举报