题解:P11294 [NOISG2022 Qualification] Tree Cutting

· · 题解

感觉大家好像都写的是 O(n)O(n\log n) 的,给大家来一个 O(n\log^2 n) 的做法养养眼(bushi),成功抢下最劣解。

简要题意

给定一个 n 个点的树,你需要断掉一条边并连接一条边,使得得到的无向图仍然构成一个树,在此基础上,最大化直径长度。输出这个最大长度。

Note:题意中没有显示要求剩下的图仍然是一棵树,但是如果不是一棵树的话“任意两城市之间的最远距离”是未定义的。 ## 思路 我们考虑枚举断掉哪一条边,为了方便,我们随便钦定一个点为根,然后枚举这条边的两端点中深度较大的点。 现在的问题是接下来连哪条边更优,我们断掉这条边后实际上将原树分成了两个连通块,那么我们显然连接两个连通块的直径最优。答案就是这两个直径的长度之和加上 $1$($1$ 表示连接的这条边的长度)。 于是我们转换成求每一个子树内 / 外的直径长度,比较好的做法是树形 dp,但是更加一般的做法是对树求一遍 dfs 序,则子树内的点位于连续区间内,用线段树维护区间直径即可。 用重链剖分求 LCA(用于求树上距离),时间复杂度 $O(n\log^2 n)$,实现精细的话换成欧拉序或 dfs 序求 LCA 可以做到 $O(n\log n)$。 ## 代码 ```cpp #include <bits/stdc++.h> #define ls (i << 1) #define rs (i << 1 | 1) #define mid ((l + r) >> 1) using namespace std; const int N = 3e5 + 5; int n; vector<int> g[N]; int top[N], dep[N], siz[N], son[N], father[N], dfn[N], rev[N]; void dfs1(int u, int fa){ dep[u] = dep[fa] + 1, father[u] = fa, siz[u] = 1; dfn[u] = ++dfn[0], rev[dfn[u]] = u; for(int v : g[u]){ if(v == fa) continue; dfs1(v, u); siz[u] += siz[v]; if(siz[v] > siz[son[u]]) son[u] = v; } } void dfs2(int u, int fa){ if(son[u]){ top[son[u]] = top[u]; dfs2(son[u], u); } for(int v : g[u]){ if(v == fa || v == son[u]) continue; top[v] = v; dfs2(v, u); } } int lca(int x, int y){ while(top[x] != top[y]){ if(dep[top[x]] < dep[top[y]]) swap(x, y); x = father[top[x]]; } return dep[x] < dep[y] ? x : y; } int dis(int x, int y){ return dep[x] + dep[y] - (dep[lca(x, y)] << 1); } struct node{ int x, y, len; } t[N << 2]; node merge(node x, node y){ vector<node> kcr = { {x.x, y.x, dis(x.x, y.x)}, {x.x, y.y, dis(x.x, y.y)}, {x.y, y.x, dis(x.y, y.x)}, {x.y, y.y, dis(x.y, y.y)}, x, y }; return *max_element(kcr.begin(), kcr.end(), [](node x, node y){ return x.len < y.len; }); } void build(int i, int l, int r){ if(l == r) return (t[i] = {rev[l], rev[l], 0}), void(); build(ls, l, mid), build(rs, mid + 1, r); t[i] = merge(t[ls], t[rs]); } node query(int ql, int qr, int i, int l, int r){ if(ql <= l && r <= qr) return t[i]; if(ql <= mid && qr > mid) return merge(query(ql, qr, ls, l, mid), query(ql, qr, rs, mid + 1, r)); else if(ql <= mid) return query(ql, qr, ls, l, mid); return query(ql, qr, rs, mid + 1, r); } signed main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin >> n; for(int i=1,u,v;i<n;i++){ cin >> u >> v; g[u].emplace_back(v), g[v].emplace_back(u); } dfs1(1, 0), dfs2(1, 0), build(1, 1, n); int ans = 0; for(int i=2;i<=n;i++){ int in_tree = query(dfn[i], dfn[i] + siz[i] - 1, 1, 1, n).len; int out_tree; if(dfn[i] != 1 && dfn[i] + siz[i] <= n) out_tree = merge(query(1, dfn[i] - 1, 1, 1, n), query(dfn[i] + siz[i], n, 1, 1, n)).len; else if(dfn[i] != 1) out_tree = query(1, dfn[i] - 1, 1, 1, n).len; else out_tree = query(dfn[i] + siz[i], n, 1, 1, n).len; ans = max(ans, in_tree + out_tree); } cout << ans + 1; return 0; } // Written by xiezheyuan ```