[CF566E] Restoring Map

[题目链接]

https://codeforces.com/contest/566/problem/E

[题解]

定义叶子节点为度数为 (1) 的节点 , 非叶节点为度数非 (1) 的节点。

首先考虑非叶节点数 (> 3) 的情况 , 两个非叶节点之间右边当且仅当能找到两个集合使得其交集为 ({x , y})

于是可以通过 (bitset) 优化枚举 , 将这部分的复杂度做到 (O(frac{N ^ 3}{w}))。(具体实现时 , 注意 (Findfirst()) 函数和 (FindNext()) 函数的使用)。

现在我们通过这种方式找到了所有非叶节点。

考虑如何找到叶子节点的父亲。

显然 , 对于每个叶子节点 , 在所有包含它的集合中 , 节点数最少的必然是它所代表的集合。

不妨称连边集合为一个非叶节点与和它相连的非叶节点构成的集合。 因为一个叶子节点去掉与其相连的叶子节点等于其父亲的连边集合 , 而连边集合互不相同 , 故可以唯一确定。这里同样可以用 (bitset) 优化。

最后来考虑非叶节点数量少于三个的情况。

如果不存在 , (N = 2)

如果只有一个 , 那么是一个菊花图的形式。

如果有两个非叶节点 ,这两个非叶节点的连边集合是一样的。此时叶子的集合只有两种,选其中一种里的叶子挂在一个非叶节点下,另一种里的叶子挂在另外一个非叶节点下即可。

时间复杂度 : (O(frac{N ^ 2}{w}))

[代码]

#include<bits/stdc++.h>
 
using namespace std;
 
typedef long long LL;
 
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
 
const int MN = 1e3 + 5;
 
int N , v[MN] , w[MN] , cnt , x , y;
bitset< MN> a[MN], b[MN], e[MN];
 
int main() {
	 
	 scanf("%d" , &N);
	 if (N == 2) {
	 	printf("1 2
");
	 	return 0;
	 }
	 bool ok = 1;
	 for (int i = 1 , j , k; i <= N; ++i) {
	 	 scanf("%d" , &k);
	 	 if (k != N) ok = 0;
	 	 while (k--) scanf("%d" , &j) , a[i][j] = 1;
	 }
	 if (ok) {
	 	for (int i = 1; i < N; ++i)
	 		printf("%d %d
" , N , i);
	 	return 0;
	 }
	 for (int i = 1; i <= N; ++i)
	 	for (int j = i + 1; j <= N; ++j) {
	 		bitset < MN > o = a[i] & a[j];
			if (o.count() != 2) continue;
			int x = o._Find_first() , y = o._Find_next(x);
			if (e[x][y]) continue;
			printf("%d %d
" , x , y); 
			e[x][y] = e[y][x] = v[x] = v[y] = 1;	
		}
	 for (int i = 1; i <= N; ++i) 
	 	 if (!v[i]) {
	 	 	 int s = N + 1 , o = 0;
			 for (int j = 1; j <= N; ++j) {
			 	 if (!w[j] && a[j][i]) {
			 	 	 int c = a[j].count();
					 if (c < s) s = c , o = j;	
				 }
			 }	
			 b[i] = a[o] , w[o] = 1;
		 } else e[i][i] = 1 , ++cnt;
	 if (cnt == 2) {
	 	 for (int i = 1; i <= N; ++i)
	 	 	if (v[i]) x = i;
	 	 for (int i = N; i >= 1; --i)
	 	 	if (v[i]) y = i;
	 	 for (int i = 1; i <= N; ++i) 
	 	 	if ((int) a[i].count() != N) {
	 	 		for (int j = 1; j <= N; ++j) {
	 	 			if (v[j]) continue;
					if (a[i][j]) printf("%d %d
" , x , j);
					else printf("%d %d
" , y , j);	
				}	
				break;
			}
		 return 0;
	 }
	 for (int i = 1; i <= N; ++i)
	 	if (!v[i]) {
	 		for (int j = 1; j <= N; ++j)
	 			if (!v[j]) b[i][j] = 0;
			for (int j = 1; j <= N; ++j)
				if (v[j] && b[i] == e[j]) {
					printf("%d %d
" , i , j);
					break;
				}		
		}
     return 0;
}
原文地址:https://www.cnblogs.com/evenbao/p/14438784.html