>Description
Treeland是一个有n个城市组成的国家,其中一些城市之间有单向边连通。在这个国家中一共有n-1条路。我们知道,如果我们不考虑路的方向,那么我可以从任意城市到达任意城市。
最近,Treeland的总理Candy为了发展经济,想要从这n个城市中选择一个作为Treeland的首都,首都必须要能到达其他任意城市,这使得有些道路必须反向,付出的代价即需要反向的道路条数。
Candy想要选择一个城市作为首都,使得付出的代价最小。可能有多个城市满足条件,按编号从小到大输出。
>Input
第一行,一个整数n,表示城市个数
接下来n-1行,每行两个整数x、y,表示城市x到城市y之间有一条单向路径
>Output
第一行,一个整数k,花费的最小代价。
第二行若干个整数,中间用空格隔开,表示满足条件的城市编号。行末没有多余的空格。
>Sample Input
Sample Input1:
3
2 1
2 3
Sample Input2:
4
1 4
2 4
3 4
>Sample Output
Sample Output1:
0
2
Sample Output2:
2
1 2 3
对于70%的数据 n<=5000
对于100%的数据 n<=2*10^5
>解题思路
如果不看题目中边的方向,路线就强制组成了一棵树。
把1(随便找个数)作为根,计算每个点到它子树中的所有节点的代价,存进s数组
然后再用一个dfs计算答案,搜到每一个点计算答案时,就会发现:除了它经过的边,到达其他点的路线(代价)都与
s
[
1
]
s[1]
s[1]相同,所以再用一个很变态的东西处理一下经过的边就好了。
>代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct tree
{
int to, next, c; //c标记这条边存不存在
} a[400005];
int n, x, y, t, ans, h[200005], s[200005], st[200005];
bool yd[200005];
void dfs (int now)
{
yd[now] = 1;
for (int i = h[now]; i; i = a[i].next)
if (!yd[a[i].to])
{
dfs (a[i].to); //dfs儿子
s[now] += s[a[i].to];
if (a[i].c == 0) s[now]++; //如果这条边不存在的话代价+1
}
yd[now] = 0;
}
void ct (int cc, int now, int dep) //cc为经过的边有多少是由根顺着下来的,now为当前节点,dep为深度(经过的边数)
{
int yes = dep - cc, no = cc; //yes为由当前节点到根有多少是顺着的,no为逆着的。下来是顺着的上去就变成了逆着的
int sum = s[1] - yes + no; //因为顺着上去的在当时计算s时是逆着下来的,所以要减去,再加上逆着上去的
if (sum < ans)
{
ans = sum;
st[0] = 0;
st[++st[0]] = now; //加入队列
}
else if (sum == ans)
st[++st[0]] = now; //累计答案
yd[now] = 1;
for (int i = h[now]; i; i = a[i].next)
if (!yd[a[i].to])
ct (cc + a[i].c, a[i].to, dep + 1);
yd[now] = 0;
}
int main()
{
scanf ("%d", &n);
for (int i = 1; i < n; i++)
{
scanf ("%d%d", &x, &y);
a[++t] = (tree) {y, h[x], 1}; h[x] = t;
a[++t] = (tree) {x, h[y], 0}; h[y] = t; //强制
}
dfs (1); //计算s
ans = 200000;
ct (0, 1, 0);
printf ("%d\n", ans);
sort (st + 1, st + 1 + st[0]);
for (int i = 1; i <= st[0]; i++)
printf ("%d ", st[i]);
return 0;
}