P2491 [SDOI2011] 树的直径 + 尺取法

题意

传送门 P2491 [SDOI2011]消防

题解

设直径的两个端点为 u , v u,v u,v。根据直径的最长性,任何从 u , p u,p u,p 之间分叉离开直径的子树,其最远点与 p p p 的距离都不会比 p p p u u u 的距离更远。

在树的直径的分叉上建立枢纽,设路径与树的直径的交点为 p p p,则 p p p 到直径端点 u , v u,v u,v 的路径至少有一条未被覆盖。根据树的直径的最长性,此时不会改变其他节点到所选择路径的距离的最大值。

那么只考虑在树的直径上建立枢纽即可。 m x [ i ] mx[i] mx[i] 代表树的直径上的节点 i i i 在不经过直径上其他节点的情况下能到达的最远点的距离,设满足条件的直径上的路径左右端点为 l , r l,r l,r,则答案为 max ⁡ ( max ⁡ i ∈ [ l , r ] { m x [ i ] } , d i s ( u , l ) , d i s ( r , v ) ) \max\Big(\max_{i\in [l,r]}\{mx[i]\},dis(u,l),dis(r,v)\Big) max(i[l,r]max{ mx[i]},dis(u,l),dis(r,v)) 根据树的直径的最长性,上式可简化为 max ⁡ ( max ⁡ i ∈ [ u , v ] { m x [ i ] } , d i s ( u , l ) , d i s ( r , v ) ) \max\Big(\max_{i\in [u,v]}\{mx[i]\},dis(u,l),dis(r,v)\Big) max(i[u,v]max{ mx[i]},dis(u,l),dis(r,v)) 使用尺取法更新答案即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 300005, maxm = maxn << 1, inf = 0x3f3f3f3f;
int N, S, num, ds[maxn], fa[maxn], mx[maxn], rec[maxn];
int tot, head[maxn], to[maxm], cost[maxm], nxt[maxm];
bool in[maxn];

inline void add(int x, int y, int z)
{
    
    
    to[++tot] = y, cost[tot] = z, nxt[tot] = head[x], head[x] = tot;
}

void dfs(int x, int f, int w, int &t)
{
    
    
    ds[x] = w, fa[x] = f;
    if (ds[x] >= ds[t])
        t = x;
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i], z = cost[i];
        if (y != f)
            dfs(y, x, w + z, t);
    }
}

void dfs2(int x, int f, int w)
{
    
    
    mx[x] = w;
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i], z = cost[i];
        if (y != f && !in[y])
            dfs2(y, x, w + z), mx[x] = max(mx[x], mx[y]);
    }
}

int main()
{
    
    
    scanf("%d%d", &N, &S);
    for (int i = 1, x, y, z; i < N; ++i)
        scanf("%d%d%d", &x, &y, &z), add(x, y, z), add(y, x, z);
    int s = 1, t = 0;
    dfs(s, 0, 0, t);
    s = t, t = 0;
    dfs(s, 0, 0, t);
    int dm = ds[t];
    for (int i = t; i; i = fa[i])
        rec[++num] = i, in[i] = 1;
    for (int i = 1; i <= num; ++i)
        dfs2(rec[i], 0, 0);
    int a = 0, res = inf;
    for (int i = 1; i <= num; ++i)
        a = max(a, mx[rec[i]]);
    int l = 1, r = 1;
    for (;;)
    {
    
    
        while (r + 1 <= num && ds[rec[l]] - ds[rec[r + 1]] <= S)
            ++r;
        res = min(res, max(a, max(dm - ds[rec[l]], ds[rec[r]])));
        if (r == num)
            break;
        ++l;
    }
    printf("%d\n", res);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/114805735