[WC2018] 通道

2022/3/31 6:21:37

本文主要是介绍[WC2018] 通道,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

[WC2018] 通道

题目描述

给你三棵树,然后每条边有边权,求一个点对 \((x,y)\) ,最大化 \(dist1(x,y)+dist2(x,y)+dist3(x,y)\),输出这个最大值。

\(n\le 10^5,w\le 10^{12}\)。

题解

这道题真的是道好题。就是有点难写。

我们首先考虑一棵树的时候,我们考虑枚举 \(lca\),然后就是问每个点的次深节点和最深节点。

考虑两棵树的时候,我们考虑两点距离可以转化为深度减去lca的深度乘上2,所以可以在第一个树上枚举lca,考虑给第二棵树上当前子树的一个点 \(i\) 一个点权 \(dep[i]\),然后就是求,在第一棵树的 \(u\) 节点的两个不同子树的点 \(x,y\) 中的在第二棵树上的路径最大值(两端点点权+路径边权)。这玩意可以通过动态维护直径(端点点权可以看作是在整个节点上挂了一个边权为点权的节点)做到 \(O(N)\) 的。

考虑三棵树的时候,就很难搞,肯定得 \(\text{polylog}\),然后考虑首先对于第一个树整个边分治,然后我们只需要考虑经过这条边的在第一个树上的点对 \((x,y)\),然后对于当前分治区域的点在第二个树上建立虚树(很套路似乎),然后考虑在虚树上做一个 \(dp\)。

咋整呢。就考虑,我们肯定是两个颜色的点 \((x,y)\),然后你要最大化 \(kz+w_x+w_y-2\times dep2_u + dep3_x+dep3_y-2\times dep3[lca3(x,y)]\),其中 \(w_x\) 表示 \(x\) 点距离分治边的距离 \(d_x\) 和 \(dep2_x\) 的和,然后 \(kz\) 是分治边的权值,\(u\) 为虚树上的 \(lca\)。

能够发现,由于我们的权值都只跟 \(x\) 有关系,可以用类似于维护两棵树的方法维护第三棵树关于两个颜色的动态直径,然后合并的时候计算答案即可。

#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
	mt19937 eng;
	void init(int Seed) { return eng.seed(Seed); }
	int rnd(int l = 1, int r = 1000000000) { return uniform_int_distribution<int> (l, r)(eng); }
}
template <typename T>
inline T min(const T &x, const T &y) { return x < y ? x : y; }
template<typename T>
inline T max(const T &x, const T &y) { return x < y ? y : x; }
template<typename T>
inline void read(T &x) {
	x = 0;
	bool f = 0;
	char ch = getchar();
	while (!isdigit(ch)) f = ch == '-', ch = getchar();
	while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
	if (f) x = -x;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
	read(x);
	read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 262144, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
	if (y < 0) {
		y %= MOD - 1;
		y += MOD - 1;
	}
	int ret = 1;
	for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
	return ret;
};
auto Mod = [] (int x) -> int {
	if (x >= MOD) return x - MOD;
	else if (x < 0) return x + MOD;
	else return x;
};
inline int ls(int k) { return k << 1; }
inline int rs(int k) { return k << 1 | 1; }
int N;
long long w[MAXN];
namespace Tree3 {
	vector<pair<int, long long>> e[MAXN];
	int clk, dfn[MAXN], st[18][MAXN];
	long long dep[MAXN];
	inline int Stmin(int x, int y) { return dfn[x] < dfn[y] ? x : y; }
	inline int lca(int x, int y) {
		x = dfn[x];
		y = dfn[y];
		if (x > y) swap(x, y);
		const int lg = std::__lg(y - x + 1);
		return Stmin(st[lg][x], st[lg][y - (1 << lg) + 1]);
	}
	void pre() {
		function<void(int, int)> dfs = [&] (int u, int lst) -> void {
			st[0][dfn[u] = ++clk] = u;
			for (auto &i: e[u]) {
				if (i.first != lst) {
					dep[i.first] = dep[u] + i.second;
					dfs(i.first, u);
					st[0][++clk] = u;
				}
			}
		};
		dfs(1, 0);
		for (int i = 1; i <= 17; ++i) {
			int l = (1 << i) / 2, limit =  clk - (1 << i) + 1;
			for (int j = 1; j <= limit; ++j) st[i][j] = Stmin(st[i - 1][j], st[i - 1][j + l]);
		}
	}
	inline long long dis(int x, int y) { return w[x] + w[y] + dep[x] + dep[y] - 2 * dep[lca(x, y)]; }
}
namespace Tree2 {
	vector<pair<int, long long>> e[MAXN];
	int clk, dfn[MAXN], st[18][MAXN], szn[MAXN];
	long long dep[MAXN];
	inline int Stmin(int x, int y) { return dfn[x] < dfn[y] ? x : y; }
	inline int lca(int x, int y) {
		if (x == y) return x;
		x = dfn[x];
		y = dfn[y];
		if (x > y) swap(x, y);
		const int lg = std::__lg(y - x + 1);
		return Stmin(st[lg][x], st[lg][y - (1 << lg) + 1]);
	}
	void pre() {
		function<void(int, int)> dfs = [&] (int u, int lst) -> void {
			st[0][dfn[u] = ++clk] = u;
			szn[u] = szn[lst] + 1;
			for (auto &i: e[u]) {
				if (i.first != lst) {
					dep[i.first] = dep[u] + i.second;
					dfs(i.first, u);
					st[0][++clk] = u;
				}
			}
		};
		dfs(1, 0);
		for (int i = 1; i <= 17; ++i) {
			int l = (1 << i) / 2, limit =  clk - (1 << i) + 1;
			for (int j = 1; j <= limit; ++j) st[i][j] = Stmin(st[i - 1][j], st[i - 1][j + l]);
		}
	}
}
namespace Tree1 {
	vector<pair<int, long long>> e[MAXN], t[MAXN];
	int ncnt;
	long long dis[MAXN];
	inline void add_edge(int x, int y, long long z) {
		t[x].push_back({y, z});
		t[y].push_back({x, z});
	}
	void pre() {
		ncnt = N;
		function<void(int, int)> dfs = [&] (int u, int lst) -> void {
			int las = u, tmp = 0, o = (int) e[u].size() - (u != 1);
			for (auto &i: e[u]) if (i.first != lst) {
				++tmp;
				if (tmp == 1) {
					add_edge(u, i.first, i.second);
					dfs(i.first, u);
				}
				else if (tmp == o) {
					add_edge(las, i.first, i.second);
					dfs(i.first, u);
				}
				else {
					add_edge(las, ++ncnt, 0);
					add_edge(las = ncnt, i.first, i.second);
					dfs(i.first, u);
				}
			}
		};
		dfs(1, 0);
	}
}
int sz[MAXN], vis[MAXN], mx[MAXN], col[MAXN], ok[MAXN];
vector<int> cyc[MAXN];
struct Path {
	int x, y;
	long long dis;
	bool operator < (const Path &a) const {
		if (!x) return 1;
		if (!a.x) return 0;
		return dis < a.dis;
	}
} f[MAXN][2];
long long ans;
Path make_path(int x, int y) {
	if (x > y) swap(x, y);
	if (!x) return {0, 0, 0};
	return {x, y, Tree3::dis(x, y)};
}
Path operator + (const Path &a, const Path &b) {
	Path ret = max(a, b);
	ret = max(ret, max(make_path(a.x, b.y), make_path(a.x, b.x)));
	ret = max(ret, max(make_path(b.x, a.y), make_path(a.y, b.y)));
	return ret;
}
long long calc(const Path &a, const Path &b) {
	long long x = max(max(make_path(a.x, b.y), make_path(a.x, b.x)), max(make_path(b.x, a.y), make_path(a.y, b.y))).dis;
	return x;
}
int l = 0;
vector<int> divide(int u, int nds) {
	l += nds;
	if (nds == 1) return u <= N ? vector<int>(1, u) : vector<int>();
	int kx = 0, ky = 0;
	long long kz = 0;
	function<void(int, int, long long)> findroot = [&] (int x, int lst, long long len) -> void {
		sz[x] = 1;
		for (auto &i: Tree1::t[x]) {
			if (i.first != lst && !vis[i.first]) {
				findroot(i.first, x, i.second);
				sz[x] += sz[i.first];
			}
		}
		mx[x] = max(sz[x], nds - sz[x]);
		if (!kx || mx[x] < mx[kx]) {
			kx = x, ky = lst, kz = len;
		}
	};
	findroot(u, 0, 0);
	int num = sz[kx];
	vis[ky] = 1;
	vector<int> tmp1 = divide(kx, sz[kx]);
	vis[ky] = 0;
	vis[kx] = 1;
	vector<int> tmp2 = divide(ky, nds - num);
	vis[kx] = 0;
	if (!tmp1.size()) return tmp2;
	if (!tmp2.size()) return tmp1;
	function<void(int, int, long long, int)> dfs = [&] (int u, int lst, long long d, int ty) -> void {
		Tree1::dis[u] = d;
		if (u <= N) col[u] = ty;
		for (auto &i: Tree1::t[u]) if (i.first != lst && !vis[i.first]) dfs(i.first, u, d + i.second, ty);
	};
	dfs(kx, ky, 0, 0);
	dfs(ky, kx, 0, 1);
	for (auto &i: tmp1) w[i] += Tree1::dis[i];
	for (auto &i: tmp2) w[i] += Tree1::dis[i];
	vector<int> tmp;
	for (int i = 0, j = 0; i < tmp1.size() || j < tmp2.size(); ) {
		if (j == tmp2.size() || (i < tmp1.size() && Tree2::dfn[tmp1[i]] < Tree2::dfn[tmp2[j]])) tmp.push_back(tmp1[i++]);
		else tmp.push_back(tmp2[j++]);
	}
	static int stk[MAXN];
	l += tmp.size();
	for (auto &i: tmp) {
		w[i] += Tree2::dep[i];
		cyc[i].clear();
		ok[i] = 1;
		if (!*stk) stk[++*stk] = i;
		else {
			int l = Tree2::lca(i, stk[*stk]);
			while (*stk > 1 && Tree2::szn[l] <= Tree2::szn[stk[*stk - 1]]) {
				cyc[stk[*stk - 1]].push_back(stk[*stk]);
				--*stk;
			}
			if (l != stk[*stk]) {
				cyc[l].clear();
				cyc[l].push_back(stk[*stk]);
				stk[*stk] = l;
			}
			stk[++*stk] = i;
		}
	}
	for (; *stk > 1; --*stk) cyc[stk[*stk - 1]].push_back(stk[*stk]);
	*stk = 0;
	function<void(int)> dp = [&] (int x) -> void {
		f[x][0] = f[x][1] = {0, 0, 0};
		if (ok[x]) f[x][col[x]] = {x, x, 0};
		for (auto &i: cyc[x]) {
			dp(i);
			ans = max(ans, max(calc(f[x][0], f[i][1]), calc(f[x][1], f[i][0])) + kz - 2 * Tree2::dep[x]);
			f[x][0] = f[x][0] + f[i][0];
			f[x][1] = f[x][1] + f[i][1];
		}
	};
	dp(stk[1]);
	for (auto &i: tmp) w[i] -= Tree2::dep[i], ok[i] = 0;
	for (auto &i: tmp1) w[i] -= Tree1::dis[i];
	for (auto &i: tmp2) w[i] -= Tree1::dis[i];
	return tmp;
}
int main() {
	freopen("347.in", "r", stdin);
	freopen("347.out", "w", stdout);
	qwq::init(20050112);
	read(N);
	long long z;
	for (int i = 1, x, y; i < N; ++i) {
		read(x, y, z);
		Tree1::e[x].emplace_back(y, z);
		Tree1::e[y].emplace_back(x, z);
	}
	for (int i = 1, x, y; i < N; ++i) {
		read(x, y, z);
		Tree2::e[x].emplace_back(y, z);
		Tree2::e[y].emplace_back(x, z);
	}
	for (int i = 1, x, y; i < N; ++i) {
		read(x, y, z);
		Tree3::e[x].emplace_back(y, z);
		Tree3::e[y].emplace_back(x, z);
	}
	Tree1::pre();
	Tree2::pre();
	Tree3::pre();
	vector<int> v = divide(1, Tree1::ncnt);
	// cerr << Tree3::lca(2, 5);
	printf("%lld\n", ans);
	// cout << (-3 / 2);
	O(l);
	cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
	return (0-0);
}


这篇关于[WC2018] 通道的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程