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;
}
标签:val,Treeland,int,题解,边权,maxn,CF219D,权值,为根 来源: https://www.cnblogs.com/whenc/p/13967585.html