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

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

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

输入格式

第一行包含一个正整数 ,表示城市 C 中的建筑和道路数目。

接下来

输出格式

包含一个实数,四舍五入保留恰好一位小数,表示最佳快餐店选址距离最远用户的距离。

注意:你的结果必须恰好有一位小数,小数位数不正确不得分。

样例一

input

4
1 2 1
1 4 2
1 3 2
2 4 1

output

2.0

explanation

最优选址为建筑

样例二

input

5
1 5 100
2 1 77
3 2 80
4 1 64
5 3 41

output

109.0

explanation

最佳选址为 的位置。

样例三

见样例数据下载。

限制与约定

对于 10% 的数据,

对于 30% 的数据,

对于 60% 的数据,

对于 100% 的数据,

时间限制:

空间限制:

分析

首先对于一棵树,显然答案等于树的直径除以2,然后我们来考虑基环树。
我们可以把基环树看做一个环+一个森林,其中每棵树的根就是这棵树和环的公共点。
显然环上总有一条边一定不会被走到,我们枚举环上的边,删掉它,然后求直径,时间复杂度,妥妥的60分。
我们能不能快速求直径呢?
我们考虑直径有哪些情况:

  1. 在树的内部
    这个我们直接预处理,然后取一个最大值,记作,因为删除环上的边对于这种情况没有影响。
  2. 经过了环上的边
    这种情况是我们重点需要解决的问题。

接下来,我们来讨论这个问题:
注意,为方便表述,以下的编号为破环为链后数组的下标
首先,破环为链(即将这个环的序列再复制一遍),成为一个序列,每次删边之后就对应序列的一个区间,我们令

l,r为当前对应的区间。
其实
那么最长链就为呢?
我们分类讨论一下
有三个点

  • 如果两个最大值不在同一处出现,假设

因为
与假设相悖
  • 如果两个最大值在同一处出现,假设出现在,所以这种情况不会被算进最优解。
  • 所以,我们可以放心地使用这种方法。时间复杂度

    代码

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define MAXN 100000
    #define INF 0x7fffffffffffffffll
    typedef long long LL;
    int n,bgc,cir[MAXN*2+10],cnt;
    bool vis[MAXN+10];
    LL ans=INF,f[MAXN+10][3],mx,sum[MAXN*2+10];
    void Read(int &x){
        char c;
        while(c=getchar(),c!=EOF)
            if(c>='0'&&c<='9'){
                x=c-'0';
                while(c=getchar(),c>='0'&&c<='9')
                    x=x*10+c-'0';
                ungetc(c,stdin);
                return;
            }
    }
    struct node{
        int v,wt;
        bool vis;
        node *next,*back;
    }*adj[MAXN+10],edge[MAXN*2+10],*ecnt=edge,*pre[MAXN+10];
    inline void addedge(int u,int v,int wt){
        node *p=++ecnt;
        p->v=v;
        p->wt=wt;
        p->vis=0;
        p->next=adj[u];
        adj[u]=p;
        p=p->back=++ecnt;
        p->v=u;
        p->wt=wt;
        p->vis=0;
        p->next=adj[v];
        adj[v]=p;
        p->back=ecnt-1;
    }
    void read(){
        Read(n);
        int i,u,v,wt;
        for(i=1;i<=n;i++){
            Read(u),Read(v),Read(wt);
            addedge(u,v,wt);
        }
    }
    void find_circle(int u){
        if(vis[u]){
            bgc=u;
            return;
        }
        vis[u]=1;
        for(node *p=adj[u];p;p=p->next){
            if(!p->vis){
                pre[p->v]=p;
                p->back->vis=p->vis=1;
                find_circle(p->v);
            }
        }
    }
    void dfs(int u,int fa){
        for(node *p=adj[u];p;p=p->next){
            if(p->v!=fa&&!p->vis){
                dfs(p->v,u);
                if(f[p->v][0]+p->wt>f[u][0]){
                    f[u][1]=f[u][0];
                    f[u][0]=f[p->v][0]+p->wt;
                }
                else if(f[p->v][0]+p->wt>f[u][1])
                    f[u][1]=f[p->v][0]+p->wt;
                f[u][2]=max(f[u][2],f[p->v][2]);
            }
        }
        f[u][2]=max(f[u][2],f[u][0]+f[u][1]);
    }
    namespace SegmentTree{
    struct node{
        LL mx,smx;
        int mxpos;
        inline node(){
        }
        inline node(LL mx,LL smx,int mxpos):mx(mx),smx(smx),mxpos(mxpos){
        }
        node *ch[2];
    }tree[MAXN*8+10],*root[2],*tcnt=tree;
    void update(node *p){
        if(p->ch[0]->mx>p->ch[1]->mx){
            p->mx=p->ch[0]->mx,p->mxpos=p->ch[0]->mxpos;
            p->smx=max(p->ch[0]->smx,p->ch[1]->mx);
        }
        else{
            p->mx=p->ch[1]->mx,p->mxpos=p->ch[1]->mxpos;
            p->smx=max(p->ch[1]->smx,p->ch[0]->mx);
        }
    }
    node merge_ans(const node &a,const node &b){
        if(a.mx>b.mx)
            return node(a.mx,max(a.smx,b.mx),a.mxpos);
        return node(b.mx,max(a.mx,b.smx),b.mxpos);
    }
    void build(node *&p,int l,int r,int ff){
        p=++tcnt;
        if(l==r){
            p->mx=f[cir[l]][0]+ff*sum[l];
            p->mxpos=l;
            p->smx=-INF;
            return;
        }
        int mid((l+r)>>1);
        build(p->ch[0],l,mid,ff);
        build(p->ch[1],mid+1,r,ff);
        update(p);
    }
    node get_ans(node *p,int l,int r,int ll,int rr){
        if(ll<=l&&r<=rr)
            return *p;
        if(ll>r||rr<l)
            return node(-INF,-INF,0);
        int mid((l+r)>>1);
        return merge_ans(get_ans(p->ch[0],l,mid,ll,rr),get_ans(p->ch[1],mid+1,r,ll,rr));
    }
    }
    void solve(){
        find_circle(1);
        int i=bgc;
        for(node *p=edge+1;p<=ecnt;p++)
            p->vis=0;
        do{
            cir[++cnt]=i;
            pre[i]->vis=pre[i]->back->vis=1;
            i=pre[i]->back->v;
        }while(i!=bgc);
        for(i=1;i<=cnt;i++){
            dfs(cir[i],0),mx=max(mx,f[cir[i]][2]);
            cir[i+cnt]=cir[i];
        }
        for(i=2;i<=2*cnt;i++)
            sum[i]=sum[i-1]+pre[cir[i-1]]->wt;
        SegmentTree::build(SegmentTree::root[0],1,cnt*2,1);
        SegmentTree::build(SegmentTree::root[1],1,cnt*2,-1);
        SegmentTree::node a,b;
        for(i=1;i<=cnt;i++){
            a=SegmentTree::get_ans(SegmentTree::root[0],1,cnt*2,i,i+cnt-1);
            b=SegmentTree::get_ans(SegmentTree::root[1],1,cnt*2,i,i+cnt-1);
            if(a.mxpos==b.mxpos)
                ans=min(ans,max(a.mx+b.smx,a.smx+b.mx));
            else
                ans=min(ans,a.mx+b.mx);
        }
        ans=max(ans,mx);
    }
    int main()
    {
        read();
        solve();
        printf("%.1lf\n",ans/2.0);
    }

    相关文章:

    • 2021-09-21
    • 2022-01-26
    • 2021-07-14
    • 2021-12-01
    • 2021-12-04
    • 2022-12-23
    猜你喜欢
    • 2021-06-22
    • 2021-10-09
    • 2021-11-10
    • 2021-05-19
    • 2021-11-08
    • 2021-11-01
    相关资源
    相似解决方案