>Description
>Input
>Output
>Sample Input
4
3 1
1 2
4 2
>Sample Output
3 2 4 1
>解题思路
输出样例其实是A从小到大对应的序号,a3<a2<a4<a1 比赛的时候没看懂TT
我们会发现,对于一个节点的对应矿洞,对应矿洞的对应矿洞也同样是这个节点,也就是说,所有矿洞2个一组,每组中每个矿洞的对应矿洞是对方
所以我们可以从叶子节点开始搜起,因为叶子节点只有一条边,连的是它的父亲,所以叶子节点的对应矿洞只能是它的父亲,它的父亲的对应矿洞只能是当前这个叶子节点
这样我们两两一组,如果有多出来没有找到对应矿洞的我们就输出-1
题目还有一个重要的性质,一个点直接连接的除了对应矿洞的所有点,它的危险程度必须都大于对应矿洞,这里可以用到拓扑排序,再加上要字典序输出,使最小的数尽可能小,我们可以对拓扑排序中入度为0的点们做一个小根堆来维护
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 500005
using namespace std;
struct line
{
int to, next;
} a[2 * N], aa[2 * N];
int n, t, h[N], tt, hh[N], g[N], r[N], ans[N], d[3 * N], cnt;
int read ()
{
int l = 0;
char c = getchar();
while (c > '9' || c < '0') c = getchar();
while (c >= '0' && c <= '9')
{
l = l * 10 + c - '0';
c = getchar();
}
return l;
}
void write (int l)
{
if (l > 9) write (l / 10);
putchar (l % 10 + '0');
}
void add (int u, int v)
{
a[++t] = (line) {v, h[u]}; h[u] = t;
a[++t] = (line) {u, h[v]}; h[v] = t;
} //建原图
void addd (int u, int v)
{
aa[++tt] = (line) {v, hh[u]}; hh[u] = tt;
} //建拓扑的图
void dfs (int now, int fath)
{
for (int i = h[now]; i; i = a[i].next)
if (a[i].to != fath)
dfs (a[i].to, now);
if (g[fath] || g[now]) return; //如果两个之中已经有一个配对了,这两个的组合就不能成立
g[now] = fath, g[fath] = now;
for (int i = h[now]; i; i = a[i].next)
if (a[i].to != fath)
r[a[i].to]++, addd (fath, a[i].to);
for (int i = h[fath]; i; i = a[i].next)
if (a[i].to != now)
r[a[i].to]++, addd (now, a[i].to); //建拓扑图
}
void up (int s)
{
while (s / 2 > 0 && d[s / 2] > d[s])
{
swap (d[s], d[s / 2]);
up (s / 2);
}
}
void down (int s)
{
int y;
while ((s * 2 <= cnt && d[s * 2] < d[s])
|| (s * 2 + 1 <= cnt && d[s * 2 + 1] < d[s]))
{
y = s * 2;
if (d[s * 2 + 1] < d[s * 2]) y++;
swap (d[s], d[y]);
down (y);
}
}
int main()
{
n = read ();
int u, v;
for (int i = 1; i < n; i++)
{
u = read (), v = read ();
add (u, v);
}
dfs (1, 0);
for (int i = 1; i <= n; i++)
if (!g[i])
{
putchar ('-'); putchar ('1');
return 0;
}
memset (d, 0x7f, sizeof (d));
for (int i = 1; i <= n; i++)
if (!r[i]) //把入度为0的点加入小根堆
{
d[++cnt] = i;
up (cnt);
}
for (int k = 1; k <= n; k++)
{
ans[k] = d[1]; //第k小的数为当前的堆顶
d[1] = d[cnt];
d[cnt] = d[0];
cnt--;
down (1);
for (int i = hh[ans[k]]; i; i = aa[i].next)
{
r[aa[i].to]--;
if (!r[aa[i].to]) //把入度为0的点加入堆
{
d[++cnt] = aa[i].to;
up (cnt);
}
}
}
for (int i = 1; i <= n; i++)
write (ans[i]), putchar (' ');
return 0;
}