E. Sergey and Subway 树形DP 换根
题目大意:
给你一颗大小为n的树,如果两个点和同一个点都有一条初始边,但是这两个点没有初始边相连,那么则可以给这两条边建一条新边(区别于初始边),问:任意两个边的和加起来的最小值是多少。
题解:
如果对题意不太理解,建议看看第二个样例。
这个题目难度不大,但是要想清楚,一开始没想清楚,状态定义的非常复杂,最后发现其实只要求每一个根节点到子节点的最短距离之和即可,然后换根求解。
(dp[u][0]) 表示在u这颗子树中,u到每一个子节点的距离和最小是多少
(dp[u][1]) 表示在u这颗子树中,u的子节点的距离和最小是多少
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 2e5+10;
typedef long long ll;
int head[maxn],to[maxn<<1],nxt[maxn<<1],cnt;
void add(int u,int v){
++cnt,to[cnt] = v,nxt[cnt] = head[u],head[u] = cnt;
++cnt,to[cnt] = u,nxt[cnt] = head[v],head[v] = cnt;
}
int siz[maxn];
ll dp[maxn][2];
void dfs1(int u,int pre){
siz[u] = 1;
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==pre) continue;
dfs1(v,u);
siz[u]+=siz[v];
dp[u][0] += dp[v][1] + siz[v];
dp[u][1] += dp[v][0];
}
// printf("dfs1:dp[%d][0]=%lld dp[%d][1]=%lld
",u,dp[u][0],u,dp[u][1]);
}
void dfs2(int u,int pre){
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==pre) continue;
ll res1 = dp[v][1],res0 = dp[v][0];
dp[v][1] += dp[u][0] - res1 - siz[v];
dp[v][0] += dp[u][1] - res0 + siz[1] - siz[v];
dfs2(v,u);
}
// printf("dfs2:dp[%d][0]=%lld dp[%d][1]=%lld
",u,dp[u][0],u,dp[u][1]);
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
dfs1(1,0),dfs2(1,0);
ll sum = 0;
for(int i=1;i<=n;i++) sum += dp[i][0];
printf("%lld
",sum/2);
return 0;
}