CF566E Restoring Map

题目链接

根本想不到这么多构造方法啊

首先我们或许可以假设 \(n \le 300\),看看能不能想到一种构造方法。

我们有一个结论:如果两个集合的公共点只有两个,那么这两个公共点一定有边直接相连。且对于非叶点间的边都可以这么找出来。(好像树的构造题总是和叶节点有关系)

这样做完以后我们就可以知道那些是叶节点,然后我们需要做的就只有挂叶节点了。

首先我们能够把叶节点的集合找出来,叶节点的集合一定是包含叶节点的最小的集合

然后试着找叶节点的父亲。我们可以处理出每个非叶点的“邻居集合”,即与该点直接相连的所有非叶点的集合(再加上自己),然后叶节点的父亲的邻居集合一定是该叶节点的集合中非叶点所组成的集合。据此可以搞出叶节点与非叶点之间的连边。

最后还要记得对 \(n \le 3\) 以及非叶点个数不足 3 的情况进行特判。

如果 \(n \le 1000\),我们可以用 bitset 来卡常。

不得不说这题真的不难理解也不难写,但是真的很难想到这些结论和方法。

关键(几乎全部)代码:

int n, etot;
bitset<N> st[N], neib[N], is_o, tmp;
int id[N];
inline void Get_o() {
	for (register int i = 1; i <= n; ++i)
		for (register int j = 1; j <= n; ++j) if (j != i) {
			tmp = st[i] & st[j];
			if (tmp.count() == 2) {
				int x = 0, y = 0;
				for (register int k = 1; k <= n; ++k)	if (tmp[k]) {
					if (x) { y = k; break; }
					x = k;
				}
				is_o[x] = is_o[y] = true;
				if (!neib[x][y])	neib[x][y] = neib[y][x] = true, ++etot, printf("%d %d\n", x, y);
			}
		}
	for (register int i = 1; i <= n; ++i)	if (!is_o[i]) {
		int mn = n + 1;
		for (register int j = 1; j <= n; ++j) if (st[j][i]) {
			int ct = st[j].count();
			if (ct < mn)	mn = ct, id[i] = j;
		}
	}
}

inline void Link_l() {
	for (register int i = 1; i <= n; ++i)	if (is_o[i])	neib[i][i] = true;
	for (register int i = 1; i <= n; ++i) if (!is_o[i]) {
		int nw = id[i];
		for (register int j = 1; j <= n; ++j)	if (st[nw][j] && !is_o[j])	st[nw][j] = false;
		for (register int j = 1; j <= n; ++j) if ((st[nw] ^ neib[j]).count() == 0) {
			printf("%d %d\n", i, j); break;
		}
	}
}

int main() {
	read(n);
	for (register int i = 1; i <= n; ++i) {
		int t; read(t);
		for (register int j = 1; j <= t; ++j) {
			int x; read(x);
			st[i][x] = true;
		}
	}
	if (n == 2) {
		puts("1 2");
		return 0;
	}
	if (n == 3) {
		puts("1 2");
		puts("2 3");
		return 0;
	}
	Get_o();
	if (!etot) {
		for (register int i = 2; i <= n; ++i)	printf("1 %d\n", i);
		return 0;
	} else if (etot == 1) {
		int fl = 1;
		while (is_o[fl])	++fl;
		int x = 0, y = 0;
		for (register int i = 1; i <= n; ++i) if (is_o[i]) {
			if (x) { y = i; break; }
			x = i;
		}
		for (register int i = 1; i <= n; ++i)	if (!is_o[i]) {
			int nw = id[i];
			if (st[nw][fl])	printf("%d %d\n", x, i);
			else	printf("%d %d\n", y, i);
		}
	} else	Link_l();
	return 0;
}
posted @ 2020-09-01 21:35  JiaZP  阅读(260)  评论(0编辑  收藏  举报