• bzoj 3242: [Noi2013]快餐店


    题目

    小T打算在城市C开设一家外送快餐店。送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小T希望快餐店的地址选在离最远的顾客距离最近的地方。 快餐店的顾客分布在城市C的N 个建筑中,这N 个建筑通过恰好N 条双向道路连接起来,不存在任何两条道路连接了相同的两个建筑。任意两个建筑之间至少存在一条由双向道路连接而成的路径。小T的快餐店可以开设在任一建筑中,也可以开设在任意一条道路的某个位置上(该位置与道路两端的建筑的距离不一定是整数)。 现给定城市C的地图(道路分布及其长度),请找出最佳的快餐店选址,输出其与最远的顾客之间的距离。

    题解

    仔细分析一下就会发现是让求一个点使这个点到最远点的距离最小.
    如果给定的是一棵树的话直接求直径的长度然后/2就好了.
    但是这次给定的是一棵基环树.
    所以考虑这棵基环树的直径.
    与树不同的是,在环上要取距离最小的一侧作为距离.
    此时我们发现 : 环上一定有至少一条边是不会被经过的.
    所以我们考虑枚举哪条边不被经过.然后计算此时树的直径.
    然后我们就知道我们所求的直径即为所有计算值的最小值.

    至于计算,我们用数组记录前缀后缀最值来统计所有经过环的路径.
    其实每次枚举的边的不同不会对不经过环的路径造成影响.
    所以统计完所有经过环的路径后单独考虑一下不经过环的路径,取最值即可.

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
        x=0;char ch;bool flag = false;
        while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
        while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    #define rg register int
    #define rep(i,a,b) for(rg i=(a);i<=(b);++i)
    #define per(i,a,b) for(rg i=(a);i>=(b);--i)
    const int maxn = 100010;
    struct Edge{
        int to,next,dis;
    }G[maxn<<1];
    int head[maxn],cnt;
    inline void add(int u,int v,int d){
        G[++cnt].to = v;
        G[cnt].next = head[u];
        head[u] = cnt;
        G[cnt].dis = d;
    }
    int fa[maxn],sta[maxn],top,fw[maxn],len[maxn];
    bool vis[maxn],inc[maxn];
    #define v G[i].to
    void dfs_cir(int u){
        vis[u] = true;
        for(rg i = head[u];i;i=G[i].next){
            if(v == fa[u]) continue;
            if(vis[v] && !inc[v]){
                int x = u;
                while(x != v){
                    inc[x] = true;
                    sta[++top] = x;
                    len[top] = fw[x];
                    x = fa[x];
                }
                inc[v] = true;
                sta[++top] = v;
                len[top] = G[i].dis;
                return ;
            }else if(!vis[v]){
                fa[v] = u;fw[v] = G[i].dis;
                dfs_cir(v);
            }
        }
    }
    ll mx[maxn],ans = 0;
    ll tree_ans = 0;
    void dfs(int u,int f){
        for(rg i = head[u];i;i=G[i].next){
            if(v == f || inc[v]) continue;
            dfs(v,u);
            tree_ans = max(tree_ans,mx[u] + G[i].dis + mx[v]);
            mx[u] = max(mx[u],mx[v] + G[i].dis);
        }
    }
    #undef v
    ll pre[maxn],suf[maxn];
    ll pre_mx[maxn],suf_mx[maxn];
    inline void work(){
        ll sum = 0,last = 0;
        rep(i,1,top){
            sum += len[i-1];
            pre[i] = max(pre[i-1],sum + mx[sta[i]]);
            pre_mx[i] = max(pre_mx[i-1],last + sum + mx[sta[i]]);
            last = max(last,mx[sta[i]] - sum);
        }
        int tmp = len[top];len[top] = 0,sum = 0,last = 0;
        per(i,top,1){
            sum += len[i];
            suf[i] = max(suf[i+1],sum + mx[sta[i]]);
            suf_mx[i] = max(suf_mx[i+1],last + sum + mx[sta[i]]);
            last = max(last,mx[sta[i]] - sum);
        }
        len[top] = tmp;
    }
    int main(){
        int n;read(n);
        int u,v,d;
        rep(i,1,n){
            read(u);read(v);read(d);
            add(u,v,d);add(v,u,d);
        }
        dfs_cir(1);
        rep(i,1,top) dfs(sta[i],sta[i]);
        work();
        ll tmp = 0,ans = pre_mx[top];
        rep(i,1,top-1){
            tmp = max(pre_mx[i],suf_mx[i+1]);
            tmp = max(tmp,pre[i] + suf[i+1] + len[top]);
            ans = min(ans,tmp);
        }
        ans = max(ans,tree_ans);
        printf("%lld.%lld
    ",ans>>1,(ans&1) ? 5LL : 0LL);
        return 0;
    }
    
  • 相关阅读:
    Luogu1053 NOIP2005篝火晚会
    BZOJ2151 种树(贪心+堆+链表/wqs二分+动态规划)
    Luogu1155 NOIP2008双栈排序(并查集)
    Luogu1092 NOIP2004虫食算(搜索+高斯消元)
    Codeforces Round#516 Div.1 翻车记
    Luogu1731 NOI1999生日蛋糕(搜索)
    洛谷 P1379 八数码难题 解题报告
    洛谷 P2501 [HAOI2006]数字序列 解题报告
    洛谷 P3143 [USACO16OPEN]钻石收藏家Diamond Collector 解题报告
    洛谷 P2894 [USACO08FEB]酒店Hotel 解题报告
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6914960.html
Copyright © 2020-2023  润新知