• luogu3761 [TJOI2017]城市


    题目

      luogu3761

    题解

      显然,只有在原树直径上删边,才可能使新树的直径变小,于是枚举直径上每条边

      算了直径复杂度也是O(n)级的,干脆直接暴力枚举所有的边

      删边后原树被分成 l, r 两颗子树,组成的新树直径有三种可能

      1. 新树的直径为子树 l 的直径

      2. 新树的直径为子树 r 的直径

      3. 设新连的边的两端点分别为 lx, rx,dis(lx/rx) 分别为 lx/rx 到子树 l/r 最远点的距离,新树的直径为 dis(lx) + dis(rx) + w(删除边的边权)

      新树的直径即为三种情况取 max

      子树 l, r 的直径可以两边bfs求得,就不多加阐述了

      对于第三种情况,首先要找到连新边的端点 lx, rx

      显然,要使 lx 到最远点的距离最短,lx 必然在树 l 的直径上,由反证法可得

      暴力枚举直径上所有点

    代码

      

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 5005;
    const int inf = 1e9;
    
    int n;
    
    struct node
    {
        int fr, to, w, nt;
        node(int ff = 0, int tt = 0, int ww = 0, int nn = 0) {fr = ff; to = tt; w = ww; nt = nn;}
    }E[N * 2];
    int num, p[N];
    
    void add(int x, int y, int z) {E[++num] = node(x, y, z, p[x]); p[x] = num;}
    
    int dis[N], fr[N], v[N]; queue<int> q;
    void bfs(int s, int bre)
    {
        memset(dis, -1, sizeof(dis));
        dis[s] = 0; q.push(s);
        while (!q.empty())
        {
            int x = q.front(); q.pop();
            for (int e = p[x]; e; e = E[e].nt)
            {
                if (e == bre || e == bre + 1) continue;
                int k = E[e].to;
                if (dis[k] == -1)
                {
                    dis[k] = dis[x] + E[e].w;
                    fr[k] = x; v[k] = E[e].w;
                    q.push(k);
                }
            }
        }
    }
    
    int deal(int id)
    {
        int x[2]; x[0] = E[id].fr, x[1] = E[id].to;
        int d[2] = {0, 0}, s[2] = {0, 0}, t[2] = {0, 0}, r[2] = {0, 0};
        for (int k = 0; k < 2; k++)
        {
            bfs(x[k], id);
            for (int i = 1; i <= n; i++) if(dis[i] > dis[s[k]]) s[k] = i;
            bfs(s[k], id);
            for (int i = 1; i <= n; i++) if(dis[i] > dis[t[k]]) t[k] = i; d[k] = dis[t[k]];
            if(t[k] == s[k]) r[k] = 0;
            else
            {
                int cnt = 0; r[k] = inf;
                while (t[k] != s[k])
                {
                    cnt += v[t[k]];
                    r[k] = min(r[k], max(d[k] - cnt, cnt));
                    t[k] = fr[t[k]];
                }
            }
        }
        return max(r[0] + r[1] + E[id].w, max(d[0], d[1]));
    }
    
    int main()
    {
        scanf("%d", &n);
        for (int i = 1; i < n; i++)
        {
            int x, y, z; scanf("%d%d%d", &x, &y, &z);
            add(x, y, z); add(y, x, z);
        }
        int ans = inf;
        for (int i = 1; i <= num; i += 2) ans = min(ans, deal(i));
        cout << ans;
        return 0;
    }
    
  • 相关阅读:
    Windows7,Ubuntu双系统,用MBR引导
    把Adblock Plus的过滤规则应用到IE9
    Linux shell学习
    vxworks下面网络连接调试的搭建
    uboot网卡成功识别
    uboot功能扩展篇
    uboot终于显示串口信息了
    uboot解决flash failed无限挂起的问题
    问题解决随笔
    琐事皆休,开始找工作~
  • 原文地址:https://www.cnblogs.com/XYZinc/p/9179384.html
Copyright © 2020-2023  润新知