思路
首先,我们根据题意建树,并给边附上权值:原有的边权值为(0),反向边权值为(1),代表走这条边所需代价。
第一次(DFS),钦定(1)为根,我们可以求出以(1)为根的答案。
第二次(DFS),考虑根由(u)转移到(v)时答案会怎么变。
- 若(u)到(v)的边权为(0),那么一开始我们是从(u)到(v),没有计算边权,所以不仅要加上从(v)到(u)的边权(1),还要把一开始没算的边权加上,总共要(+2);
- 边权为(1)同理,不过是(-2)。
设(f_u)为以(u)为根时的最小转换次数,则有:
[f_v=f_u - (val_{u->v}=1) + (val_{u -> v} = 0) quad [v in ch_u]
]
其中,(val_{u->v})表示由(u)连向(v)的边的权值。
这样,我们就可以方便地统计出答案了!
参考代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 2e5 + 10;
int n,head[maxn << 1],num;
struct Edge{
int then,to,val;
}e[maxn << 1];
void add(int u, int v, int val){e[++num] = (Edge){head[u], v, val}; head[u] = num;}
int f[maxn];
void DFS1(int u, int fa){
for(int i = head[u]; i; i = e[i].then){
int v = e[i].to;
if(v != fa){
f[u] += e[i].val;
DFS1(v, u);
f[u] += f[v];
}
}
}
void DFS2(int u, int fa){
for(int i = head[u]; i; i = e[i].then){
int v = e[i].to;
if(v != fa){
f[v] = f[u] + (e[i].val == 0) - (e[i].val == 1);
DFS2(v, u);
}
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i < n; ++ i){
int u,v; scanf("%d%d", &u, &v);
add(u, v, 0); add(v, u, 1);
}
DFS1(1, 1); DFS2(1, 1);
int Ans = 0x3f3f3f3f;
for(int i = 1; i <= n; ++ i) Ans = min(Ans, f[i]);
printf("%d
", Ans);
for(int i = 1; i <= n; ++ i)
if(f[i] == Ans) printf("%d ", i);
printf("
");
return 0;
}