zoukankan      html  css  js  c++  java
  • P1099 树网的核 && P2491 [SDOI2011]消防

    给定一棵树, 你可以在树的直径上确定一条长度不超过 (S) 的链, 使得树上离此链最长的点距离最小, 输出这个距离
    P2491 数据范围为 P1099 的 (1000)

    Solution

    首先两次 (dfs) 确定树的直径, 即第一次随意从某一点出发到达最远点记为 (s), 第二次从 (s) 出发到达最远点 (t) , 则 (s-t) 即为树的直径
    现在我们得到了直径, 试想树上现在有一条链, 包含点 (a_{1},a_{2}...a_{n}), 树上到此链最长的点无非是以下三个的最大值:

    1. (a_{1}) 到直径首的长度
    2. (a_{t}) 到直径末的长度
    3. (Max_{i = 1}^{t}d_{i})(d_{i}) 为点 (i) 在不经过直径能到达最远点的长度

    通过树的性质可得: 链上最远点长度必然包括直径到最远点的长度(才能保证最优)
    所以实际上我们只需要维护在直径上长度不超过 (S) 的链的 (1, 2) 两点, 与直径最长取最大值即可
    贪心可得在保证链长 (<=S) 的情况下越长越优
    所以维护两个指针 (l, r), 维护链上的左右端点
    枚举每一个右端点同时保持链长 (<= S) ,缩短左端点更新答案即可

    此题细节较多,看注释

    Code(P2491)

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define LL long long
    using namespace std;
    int RD(){
        int out = 0,flag = 1;char c = getchar();
        while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
        while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
        return flag * out;
        }
    const int maxn = 600019,INF = 1e9 + 19;
    int head[maxn],nume = 1;
    struct Node{
        int v,dis,nxt;
        }E[maxn << 3];
    void add(int u,int v,int dis){
        E[++nume].nxt = head[u];
        E[nume].v = v;
        E[nume].dis = dis;
        head[u] = nume;
        }
    int num, S;
    int s, t;
    int pre[maxn], dis[maxn], disl[maxn], disr[maxn], disnxt[maxn];//前驱点,非路径距离,链上距离,邻接距离
    vector<int>G;
    bool vis[maxn];
    struct Fa{int p, dis;};
    Fa get_longest(int u, int F, int tim){//函数多用
        Fa ret = (Fa){u, 0};int last = u, dn = 0;
        for(int i = head[u];i;i = E[i].nxt){
            int v = E[i].v;
            if(v == F || vis[v])continue;
            Fa ansv = get_longest(v, u, tim);
            if(ansv.dis + E[i].dis > ret.dis){
                last = v, dn = E[i].dis;
                ret.dis = ansv.dis + E[i].dis;
                ret.p = ansv.p;
                }
            }
        if(tim == 1)pre[u] = last, disnxt[u] = dn;
        return ret;
        }
    int ans = INF;
    struct Que{
        int index, val;
        }Q[maxn];
    int get_min(){
        int now = s, add = 0;
        while(pre[now] != now){
            G.push_back(now);//入链
            dis[now] = get_longest(now, -1, 0).dis;//处理非链上最长路径
            add = max(add, dis[now]);
            now = pre[now];
            }
        G.push_back(now);
        dis[now] = get_longest(now, -1, 0).dis;//处理终点
        add = max(add, dis[now]);
        int tot = 0;
        for(int i = G.size() - 1;i >= 1;i--){
            now = G[i];
            disr[now] = tot;
            tot += disnxt[G[i - 1]];
            }
        disr[s] = tot;//处理链上最长距离的另一半
        int l = 0, r = 0, len = 0;
        ans = max(add, disr[G[r]]);
        while(r < G.size()){
            len += disnxt[G[r]];r++;
            while(len > S)len -= disnxt[G[l]], l++;
            //printf("l=%d r=%d
    ", G[l], G[r]);
            now = max(disr[G[r]], disl[G[l]]);
            now = max(now, add);
            ans = min(ans, now);
            }
        ans = max(ans, add);
        return ans;
        }
    int main(){
        num = RD();S = RD();
        for(int i = 1;i <= num - 1;i++){
            int u = RD(), v = RD(), dis = RD();
            add(u, v, dis), add(v, u, dis);
            }
        s = get_longest(1, -1, 0).p, t = get_longest(s, -1, 1).p;
        int i = s, tot = 0;
        while(pre[i] != i)vis[i] = 1, disl[i] = tot, tot += disnxt[i], i = pre[i];//s到t
        vis[t] = 1, disl[t] = tot;//标记直径上的点, 处理链上最长路径
        printf("%d
    ", get_min());
        return 0;
        //for(int i = 1;i <= num;i++)if(vis[i])printf("dis[%d]=%d
    ", i, dis[i]);
        }
    
  • 相关阅读:
    元组类型内置方法
    python的两种编程模式
    Python和Python解释器
    异常处理
    文件的三种打开方式
    python解释器的安装
    编程语言的分类
    计算机基础之编程
    linux 安装postgresql
    CentOS7 部署 gitlab
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9710582.html
Copyright © 2011-2022 走看看