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;	
} 

posted @ 2021-03-03 20:56  shuitiangong  阅读(146)  评论(0编辑  收藏  举报