一些树上问题的解题报告

Cancer / 2023-07-27 / 原文

树的直径问题

luogu P3304 直径

分析

板子题,可以证明所有的直径都经过的边是几条连续的边
那么只需要找出任意一条直径,对于直径上的任意一个点,以这个点作为根节点做一次dfs
如果以该点为起点的路径(不经过原直径上的点)最长长度等于它到原直径某一段端点的长度,就说明从该点到这一端点的这一段都不是必须被经过的。
假设从直径左端到右端遍历,那么在遍历过程中找到的第一个满足  路径最长长度等于到直径右端端点长度  的点,
从该点到最右端之间的边都不是必须经过的;
而在遍历过程中找到的路径最长长度等于到直径左端端点长度的点,只能保证当前节点之前的路径是不必须经过的。

下面证明所有的直径都经过的边必然连续:
假设这样的边不连续,那么不妨在脑海里画一个图,本来是一条直径,脑海里画一条直线,根据假设,所有直径都经过的边不连续,
不妨把这条直径中间拉开,使得这条直径分为上下两边,必然满足假设的情况,然而这样必然成环,与树的结构矛盾。

#include <iostream>
#include <cstdio>
#include <vector>

using namespace std;
#define rg register
#define IOS ios::sync_with_stdio(false);cin.tie(NULL),cout.tie(NULL)
/*inline int read(){
    rg int x = 0, f = 1;
    rg char c = getchar();
    while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * f;
}*/
#define ll long long
const int N = 2e5 + 7;

struct Edge{
    int to, val, next;
}e[N << 1];
int head[N], tot;
inline void add(int u, int v, int w){
    e[++tot].to = v, e[tot].val = w, e[tot].next = head[u];
    head[u] = tot;
}

int pathleft, pathright, templ;
ll maxd, dep[N];
int f[N];
bool ondis[N];
int ansl, ansr;

void dfs1(int u, int fa){
    f[u] = fa;
    for (int i(head[u]); i; i = e[i].next){
        int w = e[i].val, v = e[i].to;
        if (v == fa) continue;
        dep[v] = dep[u] + w;
        if (dep[v] > maxd){
            maxd = dep[v];
            templ = v;
        }
        dfs1(v, u);
    }
}

void dfs2(int u, int fa){
    for (int i(head[u]); i; i = e[i].next){
        int w = e[i].val, v = e[i].to;
        if (v == fa || ondis[v]) continue;
        dep[v] = dep[u] + w;
        if (dep[v] > maxd){
            maxd = dep[v];
        }
        dfs2(v, u);
    }
}

int main(){
    IOS;
    //freopen("in.txt", "r", stdin);
    int n;
    cin >> n;
    for (int i(1); i < n; ++i){
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }

    dfs1(1, 0);
    pathleft = templ;
    dep[pathleft] = 0, maxd = 0;
    dfs1(pathleft, 0);
    pathright = templ;

    ansl = pathleft, ansr = pathright;

    cout << maxd << endl;

    for (int i(pathright); i; i = f[i]) ondis[i] = true;

    bool flag = true;
    for (int i(f[pathright]); i != pathleft; i = f[i]){
        int leftdis = dep[i], rightdis = dep[pathright] - dep[i];
        maxd = dep[i] = 0;
        dfs2(i, 0);
        if (maxd == rightdis)   ansr = i;
        else if (maxd == leftdis && flag) flag = false, ansl = i; 
    }

    int ans = 0;
    for (int i(ansr); i != ansl; i = f[i]) ++ans;
    cout << ans;
    return 0;
}