3066 快餐店

Task
有n个点,n条边,任意两点相互连通。在任意边的任意位置可设置A点,求A到n个点的距离中的最大值的最小值。

N<=1e5,边长l<=1e9

Solution
最后的答案一定在某一条边上,可以终态枚举。到每个点的距离是最短路径的问题,可以用dijksra来处理。但是怎么确定应该放在这条边的哪个位置呢?
边可以分类为链边,环边,如果在环边上dijkstra会怎么样呢?
这里写图片描述

一定会从源点从两端往中间更新。点A在一段线段中,假设左边长为x,那么右边长为d-x。
用这两个端点去dijkstra,一定会存在一个分界点,由图中不同颜色的箭头去更新。
也许可以在一条边上三分一个位置,但是30min+,无果。

如果在树上,确定一个点到每个点的距离的最大值最小,那么这个点一定是树的中心,以前树网的核中有做过,而且这个核在任意一条直径的中间。

怎么把一个基环外向树变成一棵普通的树呢?
——删去一条边。这条边就是dijkstra中一定更新不到的无用边。

因此基本的思路就是枚举断开环上的一条边,求树的直径。复杂度是O(N^2),超时了。

可以用线段树优化。
可以把环扩展2倍成链,把环上每一个点对应小树的最大距离压缩在这个点上。
最后答案为取一段长度为cirn的区间,使这个区间的直径最小。
Dis=sum[y]-sum[x-1]+mx[x]+mx[y],y>x
Sum[y]-sum[x-1]代表x到y的距离,再加上x,y各自小树对应的最长的距离,就是这个区间的直径。
可以n^2枚举这个区间的两个端点。但是也可以分治,如果把这个区间分成做区间和右区间,
那么左右端点只有可能
① 都在左区间
② 都在右区间
③ 一个在左区间,一个在右区间。
类似最优贸易简化版那道题。
最后别忘了,这个区间的直径也可能是小树的直径。

const int M=2e5+5;
ll mx[M<<1],sum[M<<1],val[M];
bool mark[M];
int id[M<<1],head[M],cnt[M],d[M];//100w
/*
    链上的点代表一棵树,mx记录直径
    val 这个点对应的小树内的直径 
    d 链上相邻两点间的距离 
*/
int n,ecnt,cirn,S,X;
struct edge{
    int t,v,nxt;
}e[M<<1];
inline void addedge(int f,int t,int v){
    e[++ecnt]=(edge){t,v,head[f]};
    head[f]=ecnt;
}
inline void input(){//建边 
    int i,j,k,a,b,c;
    rd(n);
    rep(i,1,n){
        rd(a);rd(b);rd(c);
        addedge(a,b,c);
        addedge(b,a,c);
        cnt[a]++;cnt[b]++;
    }
}
inline void Topo(){//找到不在环上的点 
    int i,j,k,x;
    queue<int>Q;
    while(!Q.empty())Q.pop();
    rep(i,1,n)if(cnt[i]==1)Q.push(i);//叶子节点
    while(!Q.empty()){
        x=Q.front();Q.pop();
        mark[x]=1;
        for(i=head[x];i;i=e[i].nxt)
            if(--cnt[e[i].t]==1)Q.push(e[i].t);
    } 
}
inline void findmx(int f,int x,ll d){
    if(d>mx[S]){X=x;mx[S]=d;}
    for(int i=head[x];i;i=e[i].nxt)
        if(e[i].t!=f&&mark[e[i].t])findmx(x,e[i].t,e[i].v+d);
}
inline void findcircle(int f,int x,int dis){
    d[cirn]=dis;
    if(x==S&&f)return;
    id[++cirn]=x;
    for(int i=head[x];i;i=e[i].nxt){
        int t=e[i].t;
        if(t==f||mark[t])continue;
        findcircle(x,t,e[i].v);
        break;
    }
}
inline void findval(int f,int x,ll d){
    if(d>val[S])val[S]=d;
    for(int i=head[x];i;i=e[i].nxt){
        int t=e[i].t;
        if(t==f||!mark[t]&&t!=id[S])continue;
        findval(x,t,d+e[i].v);
    }
}
inline void Circle(){
    int i,j,k,x,t,f;
    rep(i,1,n)if(!mark[i]){t=i;break;}//环上的点
    S=x=t;
    findcircle(0,x,0);
    rep(i,1,cirn){
        S=i;
        findmx(0,id[i],0);
        findval(0,X,0);
    }
    rep(i,cirn+1,cirn<<1){
        id[i]=id[i-cirn];
        mx[i]=mx[i-cirn];
        d[i]=d[i-cirn];
        val[i]=val[i-cirn];
    }
    rep(i,1,cirn<<1)sum[i]=sum[i-1]+d[i-1];
}
struct Tree{
    ll mi,mx,v;
    inline void clear(){
        mi=1e18;
        mx=-1;
        v=0;
    }
}ANS;
struct Segment_Tree{
    Tree t[M<<4];//480w
    inline Tree up(Tree &L,Tree &R){
        Tree C;
        C.v=max(R.mx-L.mi,max(L.v,R.v));
        C.mx=max(L.mx,R.mx);
        C.mi=min(L.mi,R.mi);
        return C;
    }
    inline void build(int l,int r,int p){
        if(l==r){
            t[p].mi=sum[l]-mx[l];
            t[p].mx=sum[l]+mx[l];
            t[p].v=val[l];
            return;
        }
        int mid=l+r>>1;
        build(l,mid,lsn(p));
        build(mid+1,r,rsn(p));
        t[p]=up(t[lsn(p)],t[rsn(p)]);
    }
    inline void query(int l,int r,int L,int R,int p){
        if(l==L&&r==R){
            ANS=up(ANS,t[p]);
            return;
        }
        int mid=L+R>>1;
        if(r<=mid)query(l,r,L,mid,lsn(p));
        else if(l>mid)query(l,r,mid+1,R,rsn(p));
        else query(l,mid,L,mid,lsn(p)),query(mid+1,r,mid+1,R,rsn(p));
    }
}T;
inline void solve(){
    int i,j,k;
    ll ans=-1;
    ANS.clear();
    rep(i,1,cirn){
        ANS.clear();
        T.query(i,i+cirn-1,1,cirn<<1,1);
//      printf("    %d %d %lld\n",i,i+cirn-1,ANS.v);
        MIN(ans,ANS.v);
    }
    db res=1.0*ans/2;
    printf("%.1lf\n",res);
}
int main(){
    /* 16.03 Today is a happy day ll 
        基环外向树
    */
//  freopen("foodshop0.in","r",stdin);
    input();
    Topo();
    Circle();
    T.build(1,cirn<<1,1);
    solve();
    return 0;
}
基于STM32F407,使用DFS算法实现最短迷宫路径检索,分为三种模式:1.DEBUG模式,2. 训练模式,3. 主程序模式 ,DEBUG模式主要分析bug,测量必要数据,训练模式用于DFS算法训练最短路径,并将最短路径以链表形式存储Flash, 主程序模式从Flash中….zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值