• 翻转树边(换根dp)


    题意

    给定一个\(n\)个节点的树,节点编号为\(1 \sim n\)。树中的\(n−1\)条边均为单向边。

    现在,我们需要选取一个节点作为中心点,并希望从中心点出发可以到达其他所有节点。

    但是,由于树中的边均为单向边,所以在选定中心点后,可能无法从中心点出发到达其他所有节点。为此,我们需要翻转一些边的方向,从而使得所选中心点可以到达其他所有节点。

    我们希望选定中心点后,所需翻转方向的边的数量尽可能少。请你确定哪些点可以选定为中心点,并输出所需的最少翻转边数量。

    数据范围

    \(2 \leq n \leq 2 \times 10^5\)

    思路

    本题是单向边,在进行树形dp的时候图是不连通的。基于这一点,我们可以建双向边,将原来的边权值设置为\(0\),表示这个方向不用翻转;将反向边的权值设置为\(1\),表示这个方向需要翻转。

    后面的过程是比较明显的换根dp。首先考虑往下走,需要翻转多少条边。我们不妨设当前点为\(u\),子节点是\(j\),往下走需要翻转的边数为\(down_u\)。则,\(down_u = \sum\limits_j (down_j + w_{uj})\)

    然后再考虑向上走需要翻转多少条边。我们不妨设当前点为\(u\),父节点为\(f\),往上走需要翻转的边数为\(up_u\)。则,\(up_u = up_f + down_j - (w_{fu} + down_u) + w_{uf}\)

    最终答案为:\(up_u + down_u\)

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 200010, M = 2 * N;
    
    int n;
    int h[N], e[M], w[M], ne[M], idx;
    int down[N], up[N];
    
    void add(int a, int b, int c)
    {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
    }
    
    void dfs1(int u, int fa)
    {
        for(int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if(j == fa) continue;
            dfs1(j, u);
            down[u] += down[j] + w[i];
        }
    }
    
    void dfs2(int u, int fa)
    {
        for(int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if(j == fa) continue;
            up[j] = up[u] + down[u] - (down[j] + w[i]) + w[i ^ 1];
            dfs2(j, u);
        }
    }
    
    int main()
    {
        scanf("%d", &n);
        memset(h, -1, sizeof h);
        for(int i = 0; i < n - 1; i ++) {
            int a, b;
            scanf("%d%d", &a, &b);
            add(a, b, 0), add(b, a, 1);
        }
        dfs1(1, -1);
        dfs2(1, -1);
        int mi = 1e9;
        for(int i = 1; i <= n; i ++) {
            mi = min(mi, up[i] + down[i]);
        }
        printf("%d\n", mi);
        for(int i = 1; i <= n; i ++) {
            if(up[i] + down[i] == mi) {
                printf("%d ", i);
            }
        }
        printf("\n");
        return 0;
    }
    
  • 相关阅读:
    pgsql 记录
    tomcat下放两个spring boot项目
    nigex 反向代理
    tomcat7里放springboot
    postgresql 建表语句
    从最大似然到EM算法浅解(转载)
    深度学习资料
    【vuejs小项目——vuejs2.0版本】组件化的开发方式
    【vuejs小项目——vuejs2.0版本】单页面搭建
    如何关闭eslint
  • 原文地址:https://www.cnblogs.com/miraclepbc/p/16334614.html
Copyright © 2020-2023  润新知