题解:CF1935F Andrey's Tree

dud_uuu 的度数,显然删掉 uu 后树变成了 dud_u 个连通块,至少需要 du1d_u-1 次合并才能变成一个连通块,所以答案下界显然是 du1d_u-1

但是下界不一定取得到。注意到这个东西其实是求最小生成树,不妨考虑 Kruskal 的贪心过程,我们肯定先希望连接形如 ii+1(i<n)i \leftrightarrow i+1(i < n) 的边,这样连接所有边后,点编号在 [1,u)[1,u)(u,n](u,n] 区间内的点内部必然已经联通了。因为假如不联通,那么你可以通过一次操作 i,i+1i,i+1 使得其联通,然而由于 uu 被删了,于是这是两个区间。

有两种情况:

  1. i,i+1i,i+1 的边连完后,全部点除了 uu 都联通,答案就是 du1d_u-1

  2. 否则,连接 u1u-1u+1u+1,答案为 dud_u

至此我们知道了答案是多少,但是怎么构造呢?

考虑将树视为以 11 为根的有根树,考虑点 iii+1i+1,其中 i<ni<n。这两个点在 u=LCA(i,i+1)u = \operatorname{LCA}(i,i+1) 的时候会需要合并,但是我们发现还有一个问题,在于合并的 i,i+1i,i+1 有时可能分别处于 uu 子树内外。

对于 uu 的每个儿子 jj,其子树内某个点 ii,如果想通过 ii+1i \leftrightarrow i+1 的边合并,且 i+1i+1uu 子树外的时候,容易发现 ii 必然是 jj 子树内 LCA(i,i+1)\operatorname{LCA}(i,i+1) 深度最小的位置,这样才有更多的 uu 能被合并。

于是我们把所有可能有贡献的点对 (i,i+1)(i,i+1) 都求出来即可。合并的时候考虑可撤销并查集。

于是现在我们要求树删掉每一个点的并查集,直接无脑上线段树分治就可以做到 O(nlog2n)O(n \log^2 n)

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
using namespace std;

const int N = 2e5 + 5;

int t, n;
vector<int> G[N];
int fa[N][21], dep[N], id[N];
int v1[N], v2[N], l1[N], l2[N];
int sz[N];

vector<pair<int, int>> ver[N];
int LG2[N];

class ST
{
public:
	int f[N][21];
	int na[N];
	void Init(int* a)
	{
		for (int i = 1; i <= n; i++) f[i][0] = i, na[i] = a[i];
		for (int j = 1; j <= LG2[n]; j++)
		{
			for (int i = 1; i + (1 << j) - 1 <= n; i++)
			{
				if (a[f[i][j - 1]] < a[f[i + (1 << (j - 1))][j - 1]]) f[i][j] = f[i][j - 1];
				else f[i][j] = f[i + (1 << (j - 1))][j - 1];
			}
		}
	}
	int query(int l, int r)
	{
		int p = LG2[r - l + 1];
		return (na[f[l][p]] < na[f[r - (1 << p) + 1][p]] ? f[l][p] : f[r - (1 << p) + 1][p]);
	}
}s1, s2;

class Union_Find
{
public:
	int fa[N], sz[N];
	void Init()
	{
		for (int i = 1; i <= n; i++) fa[i] = i, sz[i] = 1;
	}
	int find(int u)
	{
		return (fa[u] == u ? u : find(fa[u]));
	}
	pair<int, int> merge(int u, int v)
	{
		if ((u = find(u)) == (v = find(v))) return make_pair(-1, -1);
		if (sz[u] < sz[v]) swap(u, v);
		sz[u] += sz[v];
		fa[v] = u;
		return make_pair(u, v);
	}
	void rollback(pair<int, int> p)
	{
		if (p.first == -1) return;
		sz[p.first] -= sz[p.second];
		fa[p.second] = p.second;
	}
}uf;

int idx;
int ra[N];

void dfs(int u, int f)
{
	sz[u] = 1;
	id[u] = ++idx;
	ra[idx] = u;
	fa[u][0] = f;
	dep[u] = dep[f] + 1;
	for (auto& j : G[u]) if (j ^ f) dfs(j, u), sz[u] += sz[j];
}

inline int LCA(int u, int v)
{
	if (u == v) return u;
	if (dep[u] < dep[v]) swap(u, v);
	int k = dep[u] - dep[v], c = 0;
	while (k)
	{
		if (k & 1) u = fa[u][c];
		k >>= 1;
		c++;
	}
	if (u == v) return u;
	for (int i = 20; i >= 0; i--)
	{
		if (fa[u][i] ^ fa[v][i]) u = fa[u][i], v = fa[v][i];
	}
	return fa[u][0];
}

class Time_SegmentTree
{
public:
	struct Node
	{
		int l, r;
		vector<pair<int, int>> eg;
	}tr[N << 2];
	void build(int u, int l, int r)
	{
		tr[u] = { l, r };
		tr[u].eg.clear(), tr[u].eg.shrink_to_fit();
		if (l == r) return;
		int mid = l + r >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
	}
	void update(int u, int l, int r, auto x)
	{
		if (tr[u].l >= l and tr[u].r <= r)
		{
			tr[u].eg.emplace_back(x);
			return;
		}
		int mid = tr[u].l + tr[u].r >> 1;
		if (l <= mid) update(u << 1, l, r, x);
		if (r > mid) update(u << 1 | 1, l, r, x);
	}
	void solve(int u)
	{
		vector<pair<int, int>> rol;
		for (auto& [x, y] : tr[u].eg)
		{
			rol.emplace_back(uf.merge(x, y));
		}
		if (tr[u].l == tr[u].r)
		{
			int i = tr[u].l;
			vector<pair<int, int>> ans;
			vector<pair<int, int>> ds;
			for (auto& [u, v] : ver[i])
			{
				if (u == i || v == i) continue;
				ds.emplace_back(uf.merge(u, v));
				if (ds.back().first != -1) ans.emplace_back(make_pair(u, v));
			}
			for (auto& u : G[i])
			{
				if (u == fa[i][0]) continue;
				int l = id[u], r = id[u] + sz[u] - 1;
				int p1 = s1.query(l, r);
				if (v1[p1] != (int)1e9)
				{
					int j = ra[p1], j2 = j - 1;
					if (j == i || j2 == i) goto E;
					ds.emplace_back(uf.merge(j, j2));
					if (ds.back().first != -1) ans.emplace_back(make_pair(j, j2));
				}
			E:
				p1 = s2.query(l, r);
				if (v2[p1] != (int)1e9)
				{
					int j = ra[p1], j2 = j + 1;
					if (j != i && j2 != i)
					{
						ds.emplace_back(uf.merge(j, j2));
						if (ds.back().first != -1) ans.emplace_back(make_pair(j, j2));
					}
				}
			}
			int res = G[i].size() - 1;
			if (uf.sz[uf.find(i == 1 ? 2 : 1)] != n - 1)
			{
				ds.emplace_back(uf.merge(i - 1, i + 1));
				ans.emplace_back(make_pair(i - 1, i + 1));
				res++;
			}
			cout << res << " " << ans.size() << "\n";
			for (auto& [u, v] : ans)
			{
				cout << u << " " << v << "\n";
			}
			cout << "\n";
			reverse(ds.begin(), ds.end());
			for (auto& u : ds) uf.rollback(u);
			reverse(rol.begin(), rol.end());
			for (auto& u : rol) uf.rollback(u);
			return;
		}
		solve(u << 1);
		solve(u << 1 | 1);
		reverse(rol.begin(), rol.end());
		for (auto& u : rol) uf.rollback(u);
	}
}sgt;

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	for (int i = 2; i < N; i++) LG2[i] = LG2[i >> 1] + 1;
	cin >> t;
	while (t--)
	{
		cin >> n;
		for (int i = 1; i <= n; i++)
		{
			G[i].clear(), G[i].shrink_to_fit();
			v1[i] = v2[i] = (int)1e9;
			l1[i] = l2[i] = 0;
			dep[i] = 0;
			ver[i].clear(), ver[i].shrink_to_fit();
			for (int j = 0; j <= 20; j++) fa[i][j] = 0;
		}
		uf.Init();
		vector<pair<int, int>> egs;
		for (int i = 1; i < n; i++)
		{
			int u, v;
			cin >> u >> v;
			egs.emplace_back((u < v ? make_pair(u, v) : make_pair(v, u)));
			G[u].emplace_back(v);
			G[v].emplace_back(u);
		}
		idx = 0;
		dfs(1, 0);
		for (int j = 1; j <= 20; j++)
		{
			for (int i = 1; i <= n; i++) fa[i][j] = fa[fa[i][j - 1]][j - 1];
		}
		for (int i = 1; i <= n; i++)
		{
			// l1: i - 1
			if (i != 1)
			{
				l1[id[i]] = LCA(i, i - 1);
				v1[id[i]] = dep[l1[id[i]]];
			}
			if (i != n)
			{
				l2[id[i]] = LCA(i, i + 1);
				v2[id[i]] = dep[l2[id[i]]];
			}
		}
		for (int i = 1; i < n; i++)
		{
			ver[LCA(i, i + 1)].emplace_back(make_pair(i, i + 1));
		}
		s1.Init(v1);
		s2.Init(v2);
		//cout << "???: " << ver[1].size() << "\n";
		sgt.build(1, 1, n);
		for (auto& [u, v] : egs)
		{
			if (u != 1) sgt.update(1, 1, u - 1, make_pair(u, v));
			if (u + 1 < v) sgt.update(1, u + 1, v - 1, make_pair(u, v));
			if (v < n) sgt.update(1, v + 1, n, make_pair(u, v));
		}
		sgt.solve(1);
	}
	return 0;
}
posted @ 2024-03-31 19:48  HappyBobb  阅读(0)  评论(0编辑  收藏  举报  来源