[题解] CF1131F Asya And Kittens

前言

洛谷
Codeforces

题意

有一个长为 \(n\) 的排列,一开始每个数都是一个独立的联通块。有 \(n-1\) 次操作,每次要求 \(x_i\)\(y_i\) 所在的联通块相邻,然后把这两个联通块合并。求一个合法的排列使得所有操作合法。保证有解。

思路

先说这道题的坑点,合并时 \(x_i\) 必须放在左边,合并时 \(y_i\) 必须放在右边。

首先将每一个块看成一个独立的连通块。对于每一个连通块,可以看做是一条链。

于是可以使用链来维护,手写。重要的是链首和链尾。

显然初始化时,应该把每一个链的链首与链尾初始化成自己。

而对于查找每一个点属于哪一个连通块可以使用并查集进行维护。

并查集合并时,将左边的链连接至右边的链的头部,让后将左边的链尾设置尾右边的链尾。

由于右连通块的父亲将置为左连通块,所以右连通块的信息已经没有用处了,就不需要更新。

最后从整条链的链首开始,一直输出到链尾。

Code

时间复杂度尾 \(O(α(n)n)\)

#include <cstdio>
namespace Quick_Function {
	template <typename Temp> void Read(Temp &x) {
		x = 0; char ch = getchar(); bool op = 0;
		while(ch < '0' || ch > '9') { if(ch == '-') op = 1; ch = getchar(); }
		while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); }
		if(op) x = -x;
	}
	template <typename T, typename... Args> void Read(T &t, Args &... args) { Read(t); Read(args...); }
	template <typename Temp> Temp Max(Temp x, Temp y) { return x > y ? x : y; }
	template <typename Temp> Temp Min(Temp x, Temp y) { return x < y ? x : y; }
	template <typename Temp> Temp Abs(Temp x) { return x < 0 ? (-x) : x; }
	template <typename Temp> void Swap(Temp &x, Temp &y) { x ^= y ^= x ^= y; }
}
using namespace Quick_Function;
const int MAXN = 15e4 + 5;
int fa[MAXN], head[MAXN], tail[MAXN];
int nxt[MAXN], a[MAXN], n;
struct DSU {	
	int Find(int x) { return fa[x] == x ? x : (fa[x] = Find(fa[x])); }
	void Union(int x, int y) {
		int fax = Find(x), fay = Find(y);
		if(fax == fay) return;
		fa[fay] = fax;
		nxt[tail[fax]] = head[fay];
		tail[fax] = tail[fay];
	}
	void Init() { for(int i = 1; i <= n; i++) fa[i] = head[i] = tail[i] = i, nxt[i] = 0; }
	void Print(int x) {
		if(!x) return;
		printf("%d ", x);
		Print(nxt[x]);
	}
};
DSU dsu;
int main() {
	Read(n); dsu.Init();
	for(int i = 1, x, y; i < n; i++) Read(x, y), dsu.Union(x, y);
	dsu.Print(head[dsu.Find(1)]);
	return 0;
}
posted @ 2021-07-08 20:32  Last_Breath  阅读(47)  评论(1编辑  收藏  举报