[题目链接]
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;
}