CodeForces - 1494D Dogeforces(并查集)
题目大意
有一个n个点的树,每个点都有一个权值,并且父节点的权值严格大于所有子节点的权值,现在给你n个点中任意两个点的lca的权值,让你构造出来这棵树。
解题思路
因为父节点的权值严格大于所有子节点的权值,很容易想到按权值从小到大开始合并,但是有一个坑点,就是如果有几对点的lca的权值都一样的话,需要做一下处理。比如1,2,3,4这个四个点的lca的权值都一样,那么如果先和并1和2,再合并3和4,最后再合并这两组的话,就会出现父节点和子节点权值相等的情况,所以可以在权值都相等的时候对i排序,这样的话就是1和2,1和3,1和4这样合并,就不会有上面的问题了。
代码
const int maxn = 5e2+10;
const int maxm = 3e5+10;
int n, g[maxn][maxn], sal[maxm], p[maxm];
int find(int x) {
return p[x]==x ? p[x]:p[x]=find(p[x]);
}
struct I {
int u, v, w;
} a[maxm];
int main() {
cin >> n;
for (int i = 1; i<=n; ++i)
for (int j = 1; j<=n; ++j)
cin >> g[i][j];
int tot = 0;
for (int i = 1; i<=n; ++i) sal[i] = g[i][i];
for (int i = 1; i<=n; ++i)
for (int j = i+1; j<=n; ++j)
a[++tot] = {i, j, g[i][j]};
sort(a+1, a+tot+1, [](I a, I b) {return a.w==b.w ? a.u<b.u : a.w<b.w;});
for (int i = 1; i<maxm; ++i) p[i] = i;
int num = n; vector<P> ans;
for (int i = 1; i<=tot; ++i) {
int fa = find(a[i].u);
int fb = find(a[i].v);
if (fa!=fb) {
if (a[i].w>sal[fa] && a[i].w>sal[fb]) {
++num;
p[fa] = num;
p[fb] = num;
sal[num] = a[i].w;
ans.push_back({fa, num});
ans.push_back({fb, num});
}
else if (sal[fa]==a[i].w) {
p[fb] = fa;
ans.push_back({fb, fa});
}
else if (sal[fb]==a[i].w) {
p[fa] = fb;
ans.push_back({fa, fb});
}
}
}
cout << num << endl;
for (int i = 1; i<=num; ++i) printf(i==num ? "%d\n":"%d ", sal[i]);
cout << find(1) << endl;
for (auto v : ans) cout << v.x << ' ' << v.y << endl;
return 0;
}