zoukankan      html  css  js  c++  java
  • 洛谷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;
    }
    
    
  • 相关阅读:
    Java 9 模块解耦的设计策略
    Spring Data JPA 事务锁
    如何配置Spring Boot Tomcat
    Spring Cloud Turbine
    Spring Boot 测试时的日志级别
    Spring Boot中使用RSocket
    构造函数
    递归函数的使用
    有序数列的二分搜索
    Java第一次代码作业汇总
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10877525.html
Copyright © 2011-2022 走看看