BZOJ2870 最长道路tree(边分治)

BZOJ2870 最长道路tree(边分治)

题目描述

给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大。

其中链长度定义为链上点的个数。

数据范围

\(1 \le n \le 50000\)

解题思路

经典的边分治题。

首先暴力边分治会被卡成 \(n^2\) 的复杂度

所以我们需要把它建成二叉树,具体来说就是一个点第一个儿子直接连向他,剩下的建一个虚点顺次相连,每个虚点下面挂一个儿子,虚点的信息随题目的不同也不同

在这道题中,考虑边分治分成两个集合,两个集合排序然后双指针可以轻松求出答案,维护信息一定要把距离扔到边上,否则跨过虚儿子不会增加答案,另要注意分治下去的时候要记录一些全局变量的信息防止变化

代码如下

const int N = 200500;
int h[N], ne[N<<1], to[N<<1], tot, cnt;
inline void add(int x, int y) {
	ne[++tot] = h[x], to[h[x] = tot] = y;
}

ll val[N], las[N], ans, n;
namespace Conquer {
	int h[N], ne[N<<1], to[N<<1], w[N<<1], tot = 1;
	inline void add(int x, int y, int z) {
		ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
	}
	inline void adde(int x, int y, int z) { add(x, y, z), add(y, x, z); }
	
	int vis[N<<1], siz[N], Siz, lim, ed;
	void dfs(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;
			dfs(y, x), siz[x] += siz[y];
			int tp = max(siz[y], Siz - siz[y]);
			if (tp < lim) lim = tp, ed = i;
		}
	}
	pair<ll, ll> A[N], B[N], *f; 
	int *t, ta, tb;
	void dfs(int x, int fa, ll mn, ll dis) {
		Mn(mn, val[x]), f[++*t] = MP(mn, dis);
		for (int i = h[x], y; i; i = ne[i]) {
			if (vis[i] || (y = to[i]) == fa) continue;
			dfs(y, x, mn, dis + w[i]);
		}
	}
	void solve(int x, int S) {
		if (S <= 1) return;
		Siz = lim = S, dfs(x, 0), vis[ed] = vis[ed^1] = 1;
		t = &ta, ta = 0, f = A, dfs(to[ed], 0, 1e9, 0);
		t = &tb, tb = 0, f = B, dfs(to[ed^1], 0, 1e9, 0);
		sort(A + 1, A + ta + 1), sort(B + 1, B + tb + 1);
		ll j = tb, mx = 0, l = w[ed];
		for (int i = ta;i >= 1; i--) {
			while (j >= 1 && B[j].fi >= A[i].fi) Mx(mx, B[j--].se);
			if (j < tb) Mx(ans, (mx + A[i].se + 1 + l) * A[i].fi);
		}
		j = ta, mx = 0;
		for (int i = tb;i >= 1; i--) {
			while (j >= 1 && A[j].fi >= B[i].fi) Mx(mx, A[j--].se);
			if (j < ta) Mx(ans, (mx + B[i].se + 1 + l) * B[i].fi);
		}
		int tx = to[ed], ty = to[ed^1];
		solve(ty, S - siz[tx]), solve(tx, siz[tx]);
	}
}

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, 1), las[x] = x;
		else {
			val[++cnt] = val[x];
			Conquer::adde(las[x], cnt, 0);
			Conquer::adde(las[x] = cnt, y, 1);
		}
	}
}

int main() {
	read(n), cnt = n;
	for (int i = 1;i <= n; i++) read(val[i]);
	for (int i = 1, x, y;i < n; i++)
		read(x), read(y), add(x, y), add(y, x);
	dfs(1, 0), Conquer::solve(1, cnt);
	write(ans);
	return 0;
}

posted @ 2020-07-22 22:47  Hs-black  阅读(161)  评论(0编辑  收藏  举报