题解:CF1935F Andrey's Tree
设
但是下界不一定取得到。注意到这个东西其实是求最小生成树,不妨考虑 Kruskal 的贪心过程,我们肯定先希望连接形如
有两种情况:
-
把
的边连完后,全部点除了 都联通,答案就是 。 -
否则,连接
和 ,答案为 。
至此我们知道了答案是多少,但是怎么构造呢?
考虑将树视为以
对于
于是我们把所有可能有贡献的点对
于是现在我们要求树删掉每一个点的并查集,直接无脑上线段树分治就可以做到
代码:
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现