#10159. 「一本通 5.2 练习 2」旅游规划

原题传送门

题意

由于有 \(n\) 个交通路口, \(n - 1\) 条街道, 所以我们可以断定, 这是一颗树.
又由于让我们求出这棵树的最长路径, 很简单就能想到树形dp (貌似两遍 dfs 更简单?)

简化一下: 给定一棵树, 求出他的 所有 直径. (注意"所有", 这是题目的难点.)

案例

\(0\) 为根观察这棵树:
image
显然这棵树有 \(6\) 条直径, 长度为 \(5\) , 分别是
3-1-0-2-5
3-1-0-4-8
3-1-0-6-9
5-2-0-4-8
5-2-0-6-9
8-4-0-6-9

可以发现 \(0-9\) 中, 只有 \(7\) 不处于任意一条直径上, 故输出中没有 \(7\).

直径

要想做这题, 首先要会求树的直径的长度, 不会可以看这篇:如何求树的直径

路径

首先需要用 dfs1 求出直径的长度 length.
假设 \(dp[u][0]\) 表示以 \(u\) 为根的子树中最长的简单路径的长度, \(dp[u][0]\) 是以 \(u\) 为根的子树中次长的简单路径的长度.
显然, 当点 \(u\) 满足 \(dp[u][0] + dp[u][1] = length\) 时他为某一条直径上的点.这个很好找, 只需要声明一个 dfs2 遍历即可.
再从这个点出发, 寻找他的子节点, 如果子节点 \(v\) 满足 \(dp[u][0] = dp[v][0] + 1\)\(dp[u][1] = dp[v][0] + 1\) ,则说明使用了 \(dp[v]\) 更新 \(dp[u]\) 了, 即 \(v\) 也在直径上.可以用 dfs3 寻找 \(v\), 不过注意要维护 \(dp[u][0] = dp[v][0] + 1\)\(dp[u][1] = dp[v][0] + 1\) 两种情况.

代码

#include <bits/stdc++.h>
using namespace std;

inline int read() {//快读
    int x = 0; char c = getchar(); bool f = 1;
    while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return (f ? x : -x);
}

const int N = 1e6 + 10;//数组习惯开大一点
int n, res, dp[N][2];//节点数, 直径长度, dp[i][0]以i为子树中的最长路径(直径), 1为次长路径
bool vis[N];//标记是否在直径上

int cnt = -1, head[N];//链式前向星
struct edge {
    int to, nxt;
} e[N << 1];

inline void add(int u, int v) {//加边
    e[++cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}

inline void adde(int v, int u) { add(u, v); add(v, u); }

inline void dfs3(int, int, int) ;//由于dfs2在dfs3前面定义, 所以需要提前声明一下

inline void dfs(int u, int fa) {//寻找直径长度
    for(int i = head[u]; ~i; i = e[i].nxt) {
        int v = e[i].to;
        if(v == fa) continue;
        dfs(v, u);

        if(dp[u][0] < dp[v][0] + 1) { dp[u][1] = dp[u][0]; dp[u][0] = dp[v][0] + 1; }
        else if(dp[u][1] < dp[v][0] + 1) dp[u][1] = dp[v][0] + 1;
        res = max(res, dp[u][0] + dp[u][1]);
    }
}

inline void dfs2(int u, int fa) {//寻找u
    if(dp[u][0] + dp[u][1] == res) {
        dfs3(u, fa, 0);
        dfs3(u, fa, 1);
    }

    for(int i = head[u]; ~i; i = e[i].nxt) {
        int v = e[i].to;
        if(v == fa) continue;
        dfs2(v, u);
    }
}

inline void dfs3(int u, int fa, int p) {//寻找v
    vis[u] = 1;

    for(int i = head[u]; ~i; i = e[i].nxt) {
        int v = e[i].to;
        if(v == fa) continue;
        if(dp[u][p] == dp[v][0] + 1) dfs3(v, u, 0);
    }
}

int main() {
    n = read();
    for(int i = 1; i <= n; ++i) head[i] = -1;
    for(int i = 1; i < n; ++i) adde(read() + 1, read() + 1);
    dfs(1, 0);
    dfs2(1, 0);

    for(int i = 1; i <= n; ++i)//输出
        if(vis[i]) printf("%d\n", i - 1);

    return 0;
}
posted @ 2022-05-04 13:45  聂天泽  阅读(60)  评论(0编辑  收藏  举报