E. Sergey and Subway 树形DP 换根

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\n",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\n",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\n",sum/2);
    return 0;
}
posted @ 2021-02-02 22:44  EchoZQN  阅读(47)  评论(0编辑  收藏  举报