NC22598 Rinne Loves Edges

题目链接

题目

题目描述

Rinne 最近了解了如何快速维护可支持插入边删除边的图,并且高效的回答一下奇妙的询问。

她现在拿到了一个 n 个节点 m 条边的无向连通图,每条边有一个边权 \(w_i\)

现在她想玩一个游戏:选取一个 “重要点” S,然后选择性删除一些边,使得原图中所有除 S 之外度为 1 的点都不能到达 S。

定义删除一条边的代价为这条边的边权,现在 Rinne 想知道完成这个游戏的最小的代价,这样她就能轻松到达 rk1 了!作为回报,她会让你的排名上升一定的数量。

输入描述

第一行三个整数 N,M,S,意义如「题目描述」所述。

接下来 M 行,每行三个整数 u,v,w 代表点 u 到点 v 之间有一条长度为 w 的无向边。

输出描述

一个整数表示答案。

示例1

输入

4 3 1 
1 2 1 
1 3 1 
1 4 1

输出

3

说明

需要使得点 2,3,4 不能到达点 1,显然只能删除所有的边,答案为 3

示例2

输入

4 3 1 
1 2 3 
2 3 1 
3 4 2

输出

1

说明

需要使得点 4 不能到达点 1,显然删除边 \(2 \leftrightarrow 3\) 是最优的。

备注

\(2\le S \le N\le 10^{5} ,M = N-1\) ,保证答案在 C++ long long 范围内。

题解

知识点:树形dp。

注意到从 \(S\) 开始, \(S\) 是不需要考虑的,也就是说 把\(S\) 当作根是不需要考虑根节点是否度为 \(1\) ,因此考虑从 \(S\) 开始dp。

为了使度 \(1\) 的节点都到不了 \(S\) ,也就是以 \(S\) 为根的树的叶子节点必须没有到 \(S\) 的通路,可以考虑设 \(dp[u]\) 为以 \(u\) 为根的子树需要的最小花费。于是转移方程为:

\[\left \{ \begin{array}{l} dp[u] = \sum \min (dp[v_i],a[v_i]) &,deg[v_i] \neq 1\\ dp[u] = \sum a[v_i] &,deg[v_i] = 1 \end{array} \right. \]

如果子节点不是叶子节点,则可以选择断这条连子节点的边花费 \(a[v_i]\) 或者不断开使用子节点的最小花费 \(dp[v_i]\) ;如果子节点是叶子节点,则必须断边,而因为 \(dp[v_i]\) 此时为 \(0\) (因为叶子节点为根的子树里不需要断边),不能使用通式 \(\min\)

注意这里判断条件不一定需要真的求出 \(deg\) 数组,其实邻接表建图时就保存了这个节点有几条边,g[v].size() == 1 即可 。当然,也可以推断一下,如果不是叶子节点则 \(dp[v_i]\) 一定不为 \(0\) ,如果是则一定为 \(0\) ,所以也可以 \(dp[v_i]\)

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

template<class T>
struct Graph {
    int n;
    struct edge {
        int to, nxt;
        T w;
    };
    vector<edge> e;
    vector<int> h;

    explicit Graph(int _n) :n(_n), h(_n + 1, -1) {}

    void add(int u, int v, T w) {///加边
        e.push_back(edge{ v,h[u],w });///边结束节点,边出发节点的上一条边在e中下标,边权
        h[u] = e.size() - 1;///上一条边的下标
    }
};
Graph<ll> g(100007);
ll dp[100007];


void dfs(int u, int fa) {
    for (int i = g.h[u];~i;i = g.e[i].nxt) {
        int v = g.e[i].to;
        ll w = g.e[i].w;
        if (v == fa) continue;
        dfs(v, u);
        if (dp[v]) dp[u] += min(dp[v], w);///不是叶子节点一定有到叶子节点的路径,则dp[v]一定不为0
        else dp[u] += w;
    }
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m, s;
    cin >> n >> m >> s;
    for (int i = 1;i <= m;i++) {
        int u, v, w;
        cin >> u >> v >> w;
        g.add(u, v, w);
        g.add(v, u, w);
    }
    dfs(s, 0);
    cout << dp[s] << '\n';
    return 0;
}
posted @ 2022-08-23 22:57  空白菌  阅读(16)  评论(0编辑  收藏  举报