Restore the Tree 题解

CodeForces 871E

很好的一道构造题。

一个性质

我们记这 \(k\) 个点分别为 \(p_1\sim p_k\)

首先,题目并没有指定树的根,比较难受,为了方便,我们取 \(p_1\) 为根。

于是我们就意外得到了每个点的深度,也就是到 \(p_1\) 的距离 \(\left\{d_1\right\}\),我们另外记 \(i\) 的深度为 \(dep_i\)

因此,我们得到:

\[dep_{lca(u,v)}=\cfrac{dep_u+dep_v-dis_{u, v}}{2} \]

其中 \(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;
}
posted @ 2025-01-13 20:50  LightningCreeper  阅读(5)  评论(0编辑  收藏  举报