3066 快餐店

来源:互联网 发布:网络问卷调查怎么弄 编辑:程序博客网 时间:2024/04/29 04:38

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;}
0 0
原创粉丝点击