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;}
- 3066 快餐店
- NOI2013 快餐店
- [Noi2013]快餐店
- 上海快餐店一览
- 《DOS快餐店》笔记
- Bzoj-3242 快餐店(环套树)
- BZOJ3242: [Noi2013]快餐店
- bzoj3242: [Noi2013]快餐店
- 3242: [Noi2013]快餐店
- 快餐店的一个小看见
- NOI2013快餐店【图上找环+线段树】
- 【树DP+基环树】[NOI2013]快餐店
- [BZOJ3242][Noi2013]快餐店 && 环套树+线段树
- bzoj3242: [Noi2013]快餐店 树形dp+线段树
- USETC 1060 秋实大哥与快餐店
- bzoj 3242: [Noi2013]快餐店 dfs&递推
- [UESTC 1060]秋实大哥与快餐店
- 【NOI2013T6】快餐店-环套树+树形DP+线段树
- Golang仿函数实现方法及效率测试
- Android单元测试与模拟测试详解
- 我的第一节Java课
- 笨人学数学的方法
- matplotlib绘图蓝本
- 3066 快餐店
- Ubuntu+OpenCV+QT
- 关于面向对象
- Spring-05-Web-MVC注解应用
- poj 2455
- 【10月英语——带给我不一样的心境】
- Linux 用pv操作和共享内存实现生产者与消费者机制
- Android应用框架之BroadcastReceiver
- 拓展django后台