[Noi2013]快餐店

来自FallDream 的博客,未经允许,请勿转载,谢谢。


小T打算在城市C开设一家外送快餐店。送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小T希望快餐店的地址选在离最远的顾客距离最近的地方。

快餐店的顾客分布在城市C的N 个建筑中,这N 个建筑通过恰好N 条双向道路连接起来,不存在任何两条道路连接了相同的两个建筑。任意两个建筑之间至少存在一条由双向道路连接而成的路径。小T的快餐店可以开设在任一建筑中,也可以开设在任意一条道路的某个位置上(该位置与道路两端的建筑的距离不一定是整数)。

现给定城市C的地图(道路分布及其长度),请找出最佳的快餐店选址,输出其与最远的顾客之间的距离。

n<=10^5

 

一开始在那边乱敲  敲了好几K最后全删掉了   真难受

 

题意:给定一个基环外向树,求一个点(可以在边上任意位置)使得到每个点的距离的最大值最小

如果是一棵树的话 答案是直径的一半

对于外向树,直接dp,然后考虑过环的情况  

还是老做法,拆环。用s[i]表示从环上第一个点到第i个点的距离(走1.2.3...i,而不是最短路径)

考虑枚举删掉环上的一条边,求的就是max(C[i]+C[j]-s[j]+s[i]) 其中 C[i]是i所在的子树的叶子结点到根的最大距离 可以在前面dp的时候求出

这个我们可以计算前i个点,后i个点的C[i]-s[i] , C[i]+s[i] , C[i]+C[j]-s[j]+s[i]的最大值,然后枚举i求出一个最小的直径即可

最后的答案是两个情况的答案的最大值的一半  复杂度O(n)

#include<iostream>
#include<cstdio>
#include<queue>
#define MN 100000
#define INF 1000000000000000000LL
#define ll long long
using namespace std;
inline int read()
{
    int x = 0; char ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x;
}

int n,head[MN+5],top=0,q[MN+5],cnt=0,C[MN+5],cn=0,ne[MN+5],from[MN+5];
bool inq[MN+5],Found=0,mark[MN+5];
ll mx[MN+5],ans=0,f[MN+5],g[MN+5],F[MN+5],G[MN+5];
struct edge{int to,next,w;}e[MN*2+5];
inline void ins(int f,int t,int w){e[++cnt]=(edge){t,head[f],w};head[f]=cnt;}

void FindCir(int x,int fa)
{
    inq[x]=1;q[++top]=x;
    for(int i=head[x];i&&!Found;i=e[i].next)
        if(e[i].to!=fa)
        {
            if(!inq[e[i].to]) FindCir(e[i].to,x);
            else
                for(Found=1;q[top+1]!=e[i].to;--top)
                    mark[C[++cn]=q[top]]=1;
        }
    --top;inq[x]=0;
}

void dp(int x,int fa)
{
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=fa&&!mark[e[i].to])
        {
            dp(e[i].to,x);
            ans=max(ans,mx[x]+e[i].w+mx[e[i].to]);
            mx[x]=max(mx[x],mx[e[i].to]+e[i].w);
        }
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        int u=read(),v=read(),w=read();
        ins(u,v,w);ins(v,u,w);
    }
    FindCir(1,0);C[cn+1]=C[1];
    for(int i=1;i<=cn;++i)
        for(int j=head[C[i]];j;j=e[j].next)
            if(e[j].to==C[i+1])
                {ne[i]=e[j].w;break;}
    for(int i=1;i<=cn;++i)
        dp(C[i],0);
    ll sum=0,Mx=-INF,Mn=INF;f[0]=g[cn+1]=-INF;
    for(int i=1;i<=cn;++i)
    {
        sum+=ne[i-1];
        F[i]=max(F[i-1],Mx+sum+mx[C[i]]);
        f[i]=max(f[i-1],mx[C[i]]+sum);
        Mx=max(Mx,mx[C[i]]-sum);
    }
    Mx=0;sum=0;
    for(int i=cn;i;--i)
    {
        sum+=ne[i];
        G[i]=max(G[i+1],Mx+sum+mx[C[i]]);
        g[i]=max(g[i+1],mx[C[i]]+sum);
        Mx=max(Mx,mx[C[i]]-sum);
    }
    for(int i=1;i<=cn;++i) Mn=min(Mn,max(max(F[i],G[i+1]),f[i]+g[i+1]));
    printf("%.1lf\n",(double)max(Mn,ans)/2);
    return 0;
}

 

posted @ 2017-04-29 22:24  FallDream  阅读(291)  评论(0编辑  收藏  举报