Restore the Tree 题解
很好的一道构造题。
一个性质
我们记这 \(k\) 个点分别为 \(p_1\sim p_k\)。
首先,题目并没有指定树的根,比较难受,为了方便,我们取 \(p_1\) 为根。
于是我们就意外得到了每个点的深度,也就是到 \(p_1\) 的距离 \(\left\{d_1\right\}\),我们另外记 \(i\) 的深度为 \(dep_i\)。
因此,我们得到:
其中 \(dis_{x,y}\) 为 \(x\) 与 \(y\) 间的距离。
有了这个结论我们就能求一个关键点与其他任意一点的 lca 深度了。
除此之外我们还可以判断一点是不是另一点祖先。(需要满足其中一点为关键点)
构造过程
Step 0
捣鼓了半天我们连 \(p_i\) 是啥都不知道。
但是每个点到自己的距离为 \(0\)。
所以只需要看对应的距离上哪个是 \(0\) 就行了。
如果没有就直接无解。
时间复杂度 \(\Theta(nk)\)。
Step 1
由于关键点之间信息最多,先建立关键点的联系。
我们建一棵稍微小那么一点的树,其上的节点只有关键点,两点连边当且仅当其中一点为另一点在关键点中最近的祖先。
显然这是一棵以 \(p_1\) 为根的树,且拥有原树的基本形态。
要构建这棵树我们考虑枚举每个不是根的点,尝试寻找她的父亲。
只要对于每个点再枚举关键点找出深度最深且为其祖先的即可。
但是如果搞出来的不是一个树,直接无解。
时间复杂度 \(\Theta(k^2)\)。
Step 2
光有这棵树显然是没有什么用的,我们要把她还原成原树。(至少是一部分)
怎么办?
对于这棵乞丐版的树,要是两点间有连边,那她们一定有一条直上直下的链连接她们。(因为成祖先关系)
我们可以试着把这些链找出来。
考虑枚举原树上每个非关键点和小树上的每条边,看看这个点在不在这条边对应的链上。
如果这个点在链上,那她到链两端的距离和就等于链的长度。
我们把每条边上的链全部算出来,于是乎按照深度排个序就把小树的影子映射到了大树上。
如果一条链上的深度有重叠或者漏一个直接无解。
时间复杂度 \(\Theta(n\log n + nk)\)。
?
嘻嘻……你被坑了。
有没有想过一个问题,在小树下 \(u,v\) 是 \(w\) 的两个儿子,但是在大树中 \(u,v\) 的 lca 并不是 \(w\) 而是另一个点 \(p\)?
这个时候,\(w\) 到 \(p\) 之间的点会被算两次。
于是我们还要给整张图去重边!
Tip: 相较于对存图的 vector
进行 unique
,更好的方法是直接覆盖每个点的父亲。
Step 3
树的形状是出来了,但明显少了一堆点没加上去。
由于我们已经把主干画出来了,而且根一定是关键点,所以剩下的一定是挂在主干上某些节点的枝条。
先不管她枝条什么个形状,先搞清楚这些点挂在哪里。
同样枚举每个还没挂上去的点。再枚举每个关键点,她与每个关键点的 lca 中最深的那个一定是她所在枝条挂在主干上的点。
我们知道这个点深度之后,再枚举每条小树边看这个点在不在对应的链上,如果在就把这个点塞进到链上对应深度的点的 todo list 里去。
如果这个点无法挂到任何的点上去,则直接无解。
时间复杂度:\(\Theta(nk)\)。
Step 4
终于到打 boss 的阶段了!
我们现在要考虑的是,对于每个枝条,确定她的形状。
而我们唯一的限制就是每个点的深度。
直接将这些点按照深度排序,把下一深度的所有点全部接到上一深度的任意一个点上即可。
如果存在相邻深度差 \(2\) 或以上的则直接无解。
时间复杂度:\(\Theta(n\log n)\)。
总时间复杂度:\(\Theta(k^2+nk+n\log n)\)。
代码细节
看到这么多无解情况一定很头疼吧,其实,其他无解情况是无效的。
别问,问就是面向数据编程。
建议给每个小树边对应的链建 vector
这样可以在 Step 3 省很多事。
还有就是不要用 vector
或链式前向星存图,在遍历边时较为零乱,不方便去重,而且数据里的输出都是枚举每个点输出它与其父亲的边。在稍微大一点的数据进行查错时较为轻松。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define debug(x) cerr << #x << " = " << x << endl
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define gn(u, v) for (int v : G.G[u])
#define pb emplace_back
#define mp make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define pii pair<int, int>
#define vi vector<int>
#define vpii vector<pii>
#define vvi vector<vi>
#define no cout << "NO" << endl
#define yes cout << "YES" << endl
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define tomin(x, y) ((x) = min((x), (y)))
#define tomax(x, y) ((x) = max((x), (y)))
#define ck(mask, i) (((mask) >> (i)) & 1)
#define pq priority_queue
#define FLG (cerr << "Alive!" << endl);
constexpr int MAXN = 3e4 + 5;
constexpr int MAXK = 205;
int n, k;
int d[MAXK][MAXN], p[MAXK];
vi G[MAXK];
int fa[MAXK];
vpii con[MAXK];
bool mark[MAXN];
vpii hanging[MAXN];
int f[MAXN];
int dep(int x) {
return d[1][x];
}
int lcadep(int x, int y) {
return (dep(p[x]) + dep(p[y]) - d[x][p[y]]) / 2;
}
int lca(int x, int y) {
return (dep(p[x]) + dep(y) - d[x][y]) / 2;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
rep (i, 1, k) {
bool flag = false;
rep (j, 1, n) {
cin >> d[i][j];
if (d[i][j] == 0) {
p[i] = j;
mark[p[i]] = true;
flag = true;
}
}
if (!flag) {
cout << -1 << endl;
return 0;
}
// cerr << p[i] << " ";
}
// cerr << endl;
rep (i, 1, k) {
rep (j, 1, k) {
if (i == j)
continue;
if (lcadep(i, j) == dep(p[i])) {
if (dep(p[i]) > dep(p[fa[j]]) || !fa[j]) {
fa[j] = i;
}
}
}
}
// rep (i, 2, k) {
// cerr << p[i] << " " << p[fa[i]] << endl;
// }
rep (i, 1, n) {
rep (j, 2, k) {
if (p[j] != i && p[fa[j]] != i) {
if (d[j][i] + d[fa[j]][i] == d[j][p[fa[j]]] && dep(i) > dep(p[fa[j]]) && dep(i) < dep(p[j])) {
// cerr << p[j] << "." << dep(p[j]) - dep(p[fa[j]]) << " ";
con[j].pb(mp(dep(i), i));
mark[i] = true;
}
}
}
// cerr << endl;
}
rep (i, 2, k) {
sort(all(con[i]));
int last = p[fa[i]];
for (auto [ord, u] : con[i]) {
f[u] = last;
last = u;
}
f[p[i]] = last;
// cerr << endl;
}
// rep (i, 1, n) {
// if (mark[i]) {
// cerr << i << " ";
// }
// }
// cerr << endl;
rep (i, 1, n) {
if (mark[i])
continue;
int mxdep = -1, point;
// cerr << endl;
rep (j, 1, k) {
if (lca(j, i) > mxdep) {
mxdep = lca(j, i);
point = j;
// cerr << j << " ";
} else if (lca(j, i) == mxdep) {
// cerr << j << " ";
if (dep(p[point]) > dep(p[j]))
point = j;
}
}
// if (i == 8) {
// cerr << point << " " << mxdep << endl;
// }
// cerr << i << ":" << point << " " << mxdep << endl;
if (lca(point, i) == dep(p[point])) {
hanging[p[point]].pb(mp(dep(i), i));
continue;
}
bool flag = true;
mxdep = 0;
rep (j, 2, k) {
if (lca(j, i) > dep(p[fa[j]]) && lca(j, i) < dep(p[j])) {
int cur = con[j][lca(j, i) - dep(p[fa[j]]) - 1].se;
if (dep(cur) > dep(mxdep)) {
mxdep = cur;
}
// cerr << lca(j, i) << endl;
// cerr << con[j][lca(j, i) - dep(p[fa[j]]) - 1].fi
}
}
hanging[mxdep].pb(mp(dep(i), i));
// if (flag) {
// cerr << i << endl;
// }
}
rep (i, 1, n) {
if (mark[i]) {
sort(all(hanging[i]));
// if (hanging[i].size()) {
// cerr << i << ":" << endl;
// for (auto [ord, u] : hanging[i]) {
// cerr << u << " ";
// }
// cerr << endl;
// }
int cur = i, last = i;
for (auto [ord, u] : hanging[i]) {
if (dep(u) != dep(last)) {
cur = last;
}
f[u] = cur;
mark[u] = true;
// cerr << cur << " " << u << endl;
last = u;
}
}
}
rep (i, 1, n)
if (f[i])
cout << i << " " << f[i] << endl;
return 0;
}