洛谷P3629 [APIO2010]巡逻(树的直径)

如果考虑不算上新修的道路,那么答案显然为\(2*(n-1)\)

考虑\(k=1\)的情况,会发现如果我们新修建一个道路,那么就会有一段路程少走一遍。这时选择连接树的直径的两个端点显然是最优的。

难就难在\(k=2\)的时候,还是上面的思路,首先肯定连接两个叶子结点最优。假设我们连接的是\(x,y\)两个叶子结点,它们到直径的距离分别为\(dis[x],dis[y]\),并设直径上两点的距离为\(d[u,v]\),这里\(u,v\)分别为叶子结点所在链和直径的交点。

因此最后的答案会增加\(d[u,v]-dis[x]-dis[y]\)。要使答案最小,那么也就也是使得\(dis[x]+dis[y]-d[u,v]\)最大。脑补一下,就会发现这其实就是在所有直径上面的边权取反过后,树的最长链。

所以再求一次树的直径就好了。因为最后有负边权存在,通过\(dfs/bfs\)来求会出错。所以最后dp一次就好啦。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, k;
struct Edge{
    int u, v, next, w;
}e[N << 1];
int head[N], tot;
void adde(int u, int v) {
    e[tot].w = 1; e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
}
int vis[N], f[N], d[N], dp[N];
void dfs(int u, int fa) {
    f[u] = fa;
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if(v != fa) {
            d[v] = d[u] + e[i].w;
            dfs(v, u) ;
        }
    }
}
int mx, p, L;
void Get(int x) {
    d[x] = mx = 0;
    dfs(x, 0);
    for(int i = 1; i <= n; i++)
        if(d[i] > mx) mx = d[i], p = i;
}
int solve() {
    Get(1);Get(p);
    return mx;
}
void dfs2(int u, int fa) {
    vis[u] = 1;
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if(v == fa || !vis[v]) continue ;
        e[i].w = e[i ^ 1].w = -1;
        dfs2(v, u) ;
    }
}
void Dp(int u, int fa) {
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if(v == fa) continue ;
        Dp(v, u);
        L = max(L, dp[u] + dp[v] + e[i].w) ;
        dp[u] = max(dp[u], dp[v] + e[i].w) ;
    }
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> k;
    memset(head, -1, sizeof(head)) ;
    for(int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        adde(u, v); adde(v, u);
    }
    int l = solve() ;
    int ans = 2 * (n - 1) - l + 1;
    if(k == 2) {
        int u = p;
        while(u != 0) {
            vis[u] = 1;
            u = f[u];
        }
        dfs2(p, 0) ;
        Dp(1, 0) ;
        ans = ans - L + 1;
    }
    cout << ans ;
    return 0;
}

posted @ 2019-05-16 19:26  heyuhhh  阅读(140)  评论(0编辑  收藏  举报