CodeForces 1935F Andrey's Tree

洛谷传送门

CF 传送门

赛后 15min 过题/ll。

删掉点 \(u\) 后树会分成若干棵子树。给每个子树一个编号,令 \(c_i\) 表示 \(i\) 所在子树的编号。然后题目要求一个类似最小生成树的东西。

既然要求最小生成树,那肯定先从 \(|a - b| = 1\) 选起。对于所有 \(i \in [1, u - 2] \cup [u + 1, n]\),连边 \((c_i, c_{i + 1})\),那么新图上最多有两个连通块。

新图只有一个连通块就直接做完了,否则 \((c_{u - 1}, c_{u + 1})\) 一定不在同一个连通块,合并它们的代价是 \(2\)。这样就是最优策略。

考虑怎么快速找到全部有用的 \((c_i, c_{i + 1})\) 的边。对于一对 \((i, i + 1)\),设 \(x = \text{lca}(i, i + 1)\)。发现只有当 \(u = x\)\(i, i + 1\) 才都在 \(x\) 的子树中且在 \(x\) 的不同儿子的子树。设 \(u\) 的一个儿子 \(y\)\(i\) 所在子树,那么 \(fa_i \to y\) 路径上的所有点满足 \(i\) 在它的一个儿子的子树,\(i + 1\) 在它父亲的子树。反过来对于 \(i + 1\) 也类似。

\(u = x\) 时可以用 vector 存全部满足的 \(i\);否则维护每个点 \(v\) 的子树中的使得 \(dep_z\) 最小的 \(i\)(这样就能尽可能使得 \(i + 1\)\(v\) 父亲那棵子树)。用可撤销并查集维护连通块的合并。

总时间复杂度 \(O(n \log n)\)

code
// Problem: F. Andrey's Tree
// Contest: Codeforces - Codeforces Round 932 (Div. 2)
// URL: https://codeforces.com/contest/1935/problem/F
// Memory Limit: 256 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 200100;
const int logn = 20;

int n, ff[maxn], rk[maxn], tp;
pair<int*, int> stk[maxn << 2];
pii b[maxn];
vector<int> G[maxn];
vector<pii> T[maxn];

int fa[maxn], sz[maxn], son[maxn], dep[maxn], f[logn][maxn];
int top[maxn], dfn[maxn], tim, rnk[maxn];
pii g[logn][maxn];

inline pii qmin(int l, int r) {
	int k = __lg(r - l + 1);
	return min(g[k][l], g[k][r - (1 << k) + 1]);
}

int dfs(int u, int f, int d) {
	fa[u] = f;
	sz[u] = 1;
	dep[u] = d;
	int mx = -1;
	for (int v : G[u]) {
		if (v == f) {
			continue;
		}
		::f[0][v] = u;
		sz[u] += dfs(v, u, d + 1);
		if (sz[v] > mx) {
			son[u] = v;
			mx = sz[v];
		}
	}
	return sz[u];
}

void dfs2(int u, int tp) {
	top[u] = tp;
	dfn[u] = ++tim;
	rnk[tim] = u;
	if (!son[u]) {
		return;
	}
	dfs2(son[u], tp);
	for (int v : G[u]) {
		if (!dfn[v]) {
			dfs2(v, v);
		}
	}
}

inline int qlca(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) {
			swap(x, y);
		}
		x = fa[top[x]];
	}
	if (dep[x] > dep[y]) {
		swap(x, y);
	}
	return x;
}

inline int jump(int x, int k) {
	for (int i = 18; ~i; --i) {
		if (k & (1 << i)) {
			x = f[i][x];
		}
	}
	return x;
}


int find(int x) {
	return ff[x] == x ? x : find(ff[x]);
}

inline bool merge(int x, int y) {
	x = find(x);
	y = find(y);
	if (x == y) {
		return 0;
	}
	if (rk[x] <= rk[y]) {
		stk[++tp] = mkp(ff + x, ff[x]);
		ff[x] = y;
		if (rk[x] == rk[y]) {
			stk[++tp] = mkp(rk + y, rk[y]);
			++rk[y];
		}
	} else {
		stk[++tp] = mkp(ff + y, ff[y]);
		ff[y] = x;
	}
	return 1;
}

void solve() {
	scanf("%d", &n);
	tim = tp = 0;
	for (int i = 1; i <= n; ++i) {
		ff[i] = i;
		rk[i] = 0;
		vector<int>().swap(G[i]);
		vector<pii>().swap(T[i]);
		fa[i] = sz[i] = son[i] = dep[i] = top[i] = dfn[i] = 0;
		g[0][i] = mkp(n + 1, n + 1);
	}
	for (int i = 1, u, v; i < n; ++i) {
		scanf("%d%d", &u, &v);
		G[u].pb(v);
		G[v].pb(u);
	}
	dfs(1, -1, 1);
	dfs2(1, 1);
	for (int j = 1; j <= 18; ++j) {
		for (int i = 1; i <= n; ++i) {
			f[j][i] = f[j - 1][f[j - 1][i]];
		}
	}
	for (int i = 1; i < n; ++i) {
		int x = i, y = i + 1, z = qlca(i, i + 1);
		if (dep[x] > dep[y]) {
			swap(x, y);
		}
		if (dep[x] - dep[z] >= 2) {
			g[0][dfn[x]] = min(g[0][dfn[x]], mkp(dep[z] + 1, i));
		}
		if (dep[y] - dep[z] >= 2) {
			g[0][dfn[y]] = min(g[0][dfn[y]], mkp(dep[z] + 1, i));
		}
		if (x != z) {
			T[z].pb(i, i + 1);
		}
	}
	for (int j = 1; (1 << j) <= n; ++j) {
		for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
			g[j][i] = min(g[j - 1][i], g[j - 1][i + (1 << (j - 1))]);
		}
	}
	for (int i = 1; i <= n; ++i) {
		vector<pii> res;
		int ans = 0;
		for (pii p : T[i]) {
			int x = p.fst, y = p.scd;
			int u = jump(x, dep[x] - dep[i] - 1), v = jump(y, dep[y] - dep[i] - 1);
			if (merge(u, v)) {
				++ans;
				res.pb(x, y);
			}
		}
		for (int j : G[i]) {
			if (j == fa[i]) {
				continue;
			}
			pii p = qmin(dfn[j], dfn[j] + sz[j] - 1);
			if (p.fst <= dep[i] && merge(j, i)) {
				++ans;
				res.pb(p.scd, p.scd + 1);
			}
		}
		if (ans != (int)G[i].size() - 1) {
			ans += 2;
			res.pb(i - 1, i + 1);
		}
		printf("%d %d\n", ans, (int)res.size());
		for (pii p : res) {
			printf("%d %d\n", p.fst, p.scd);
		}
		putchar('\n');
		while (tp) {
			*stk[tp].fst = stk[tp].scd;
			--tp;
		}
	}
}

int main() {
	int T = 1;
	scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2024-03-09 17:56  zltzlt  阅读(34)  评论(0编辑  收藏  举报