Codeforces 1494D Dogeforces(构造+并查集)

传送门


题目大意

给出一棵带权树,其中所有父节点的权值一定严格大于子节点。给出了所有叶子节点的权值以及两两叶子之间的 最近公共祖先 L C A LCA LCA 的权值,试着还原这棵树。

解题思路

树的构造问题大都从特殊到一般、从叶子结点开始构造。

对于两个叶子结点,显然第一次操作时只需要新建一个节点,然后将他们连接起来;对于两棵分别连好了两个叶子节点的子树,如下图所示,若 1    4 1~~4 1  4 L C A LCA LCA 的权值大于了两个父节点 5 , 6 5,6 5,6,那么我们新建一个 7 7 7 节点连接上这两棵子树。

在这里插入图片描述
这个时候似乎已经可以窥见构造规律了,对于每对给出的 L C A LCA LCA信息,从小到大构造。上述子树还会有两种情况,也就是当前的 L C A LCA LCA等于两棵子树的某个根节点,那么将另一棵子树连上去即可。至于如何维护一棵子树的根节点,使用并查集,但是并查集只是维护两个集合的,怎么保证集合的祖先就是根节点?实际上只需要设置当前集合祖先时更新为根节点,这样在路径压缩的时候祖先节点的信息不会改变,只会将下面子节点的祖先节点更新,真是妙用!

易错点: 对于一个节点的子树下若有很多节点,如何保证不生成两个相同的父节点(对于本题的贪心思路来说,尽可能使得相同的 L C A LCA LCA 连接到同一根节点下),那么对于相同的 L C A LCA LCA 权值,排序时先根据序号较小的节点升序然后根据序号较大的节点升序。例如 1    2    3    4 1~~2~~3~~4 1  2  3  4 L C A LCA LCA都相同,显然 1    2 1~~2 1  2 1    3 1~~3 1  3 1    4 1~~4 1  4…这样处理就不会出现上述问题。

reference:https://blog.csdn.net/liufengwei1/article/details/114298063

#include <bits/stdc++.h>

using namespace std;
#define ENDL "\n"
typedef long long ll;
const int maxn = 5e5 + 10;

struct node {
    
    
    int u, v, w;

    bool operator<(const node &p) const {
    
    
        if (w == p.w) {
    
    
            if (u == p.u) return v < p.v;
            return u < p.u;
        }
        return w < p.w;
    }
};

vector<node> res;
int f[maxn], father[maxn], val[maxn];

int Find(int x) {
    
     return f[x] == x ? x : f[x] = Find(f[x]); }

int main() {
    
    
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n * n; i++) f[i] = i;
    for (int i = 1, x; i <= n; i++) {
    
    
        for (int j = 1; j <= n; j++) {
    
    
            cin >> x;
            if (j > i)
                res.push_back({
    
    i, j, x});
            else if (i == j)
                val[i] = x;
        }
    }
    sort(res.begin(), res.end());
    int idx = n + 1;
    for (auto p : res) {
    
    
        int fu = Find(p.u), fv = Find(p.v);
        if (fu == fv) continue;
        int Max = max(val[fu], val[fv]);
        if (Max == p.w) {
    
    
            if (p.w == val[fu])
                father[fv] = f[fv] = fu;
            else
                father[fu] = f[fu] = fv;
        } else {
    
    
            father[fu] = father[fv] = idx;
            val[idx] = p.w;
            f[fu] = f[fv] = idx;
            idx++;
        }
    }
    cout << idx - 1 << ENDL << val[1];
    for (int i = 2; i <= idx - 1; i++) cout << ' ' << val[i];
    cout << ENDL;
    cout << idx - 1 << ENDL;
    for (int i = 1; i <= idx - 2; i++) {
    
    
        if (father[i]) cout << i << " " << father[i] << ENDL;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/115521667