CF219D Choosing Capital for Treeland(换根DP)题解

思路

首先,我们根据题意建树,并给边附上权值:原有的边权值为\(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\n", Ans);
    for(int i = 1; i <= n; ++ i)
        if(f[i] == Ans) printf("%d ", i);
    printf("\n");
    return 0;
}
posted @ 2020-11-13 09:29  When_C  阅读(90)  评论(0编辑  收藏  举报