NOI2013 快餐店
来源:互联网 发布:根据数据库生成网页 编辑:程序博客网 时间:2024/06/03 00:18
NOI2013 快餐店
Description
小T打算在城市C开设一家外送快餐店。送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小T希望快餐店的地址选在离最远的顾客距离最近的地方。
快餐店的顾客分布在城市C的N 个建筑中,这N 个建筑通过恰好N 条双向道路连接起来,不存在任何两条道路连接了相同的两个建筑。任意两个建筑之间至少存在一条由双向道路连接而成 的路径。小T的快餐店可以开设在任一建筑中,也可以开设在任意一条道路的某个位置上(该位置与道路两端的建筑的距离不一定是整数)。
现给定城市C的地图(道路分布及其长度),请找出最佳的快餐店选址,输出其与最远的顾客之间的距离。
Input
第一行包括一个整数n表示城市C中建筑物与道路的数目
接下来N行,每行包括三个整数Ai,Bi,Li(1<=i<=n,Li>0)表示一条道路连接了Ai,Bi,其长度为Li
Output
输出仅为一个实数四舍五入保留恰好一位小数表示最佳快餐店选址离最远用户的距离
Sample Input
【样例输入1】
4
1 2 1
1 4 2
1 3 2
2 4 1
【样例输入2】
5
1 5 100
2 1 77
3 2 80
4 1 64
5 3 41
Sample Output
【样例输出1】
2.0
【样例输出2】
109.0
Hint
对于 10%的数据,N<=80,Li=1;
对于 30%的数据,N<=600,Li<=100;
对于 60% 的数据,N<=2000,Li<=10^9;
对于 100% 的数据,N<=10^5,Li<=10^9
Solution:
首先
60分是非常好写的,因为这个图里有且仅有一个环,因此这个环上肯定有一条边是不走的。因此直接枚举这条边,剩下的就是一棵树了,显然应该建在树的重心上,答案就是
#include<ctime>#include<cstdio>#include<cctype>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>#include<set>#include<map>#include<queue>#include<vector>#include<string>#define ll long long#define lson (p<<1)#define rson (p<<1|1)#define lowbit(x) ((x)&(-x))#define siz(x) ((int)(x).size())using namespace std;inline void Rd(int &res){ char c;res=0; while(c=getchar(),c<'0'); do{ res=(res<<1)+(res<<3)+(c^48); }while(c=getchar(),c>='0');}const int M=(int)1e5+5;struct Edge{int to,w,nxt;}Edge[M<<1];int Head[M],tot,degree[M];int n;inline void Addedge(int a,int b,int c){ Edge[++tot].to=b;Edge[tot].w=c;Edge[tot].nxt=Head[a];Head[a]=tot; Edge[++tot].to=a;Edge[tot].w=c;Edge[tot].nxt=Head[b];Head[b]=tot;}struct Q{int a,b;}Q[M];struct P60{ int Igra,Igrb,id; ll mx; bool vis[M],flag; void dfs(int x,int f,ll d){ if(d>mx)mx=d,id=x; if(vis[x]){//环 flag=false; return; } vis[x]=true; for(int i=Head[x];~i;i=Edge[i].nxt){ int to=Edge[i].to; if(to==f)continue; if(to==Igra&&x==Igrb)continue; if(to==Igrb&&x==Igra)continue; dfs(to,x,d+Edge[i].w); } } void solve(){ double ans=-1; for(int i=1;i<=n;i++){ memset(vis,0,sizeof(vis)); Igra=Q[i].a;Igrb=Q[i].b; flag=true; mx=-1;dfs(Q[i].a,0,0); for(int j=1;j<=n;j++) if(!vis[j]){flag=false;break;}//不连通 if(!flag)continue; memset(vis,0,sizeof(vis)); mx=-1;dfs(id,0,0); if(ans==-1||mx/2.0<ans)ans=mx/2.0; } printf("%.1f\n",ans); }}P60;int main(){// freopen("foodshop.in","r",stdin);// freopen("foodshop.out","w",stdout); memset(Head,-1,sizeof(Head)); Rd(n); for(int i=1;i<=n;i++){ int a,b,c; Rd(a),Rd(b),Rd(c); Q[i].a=a;Q[i].b=b; Addedge(a,b,c); } if(n<=2000)P60.solve();}
然后考虑正解,将环抽出来并复制一份成为两倍长度的链。仍然考虑断边,最后的答案有可能来自外向树树中的直径或者是经过环上边的最长路径。前者直接
其中
如果不考虑
当我们断开一条环上的边
于是我们建立两颗线段树,分别维护
但是这里还有一个问题,我们Query出来的两个点的下标有可能是相同的,而这时实际上不合法的。于是在线段树节点上同时保存最大值与次大值就可以了。
于是这题就可以用线段树解决了,复杂度
#include<ctime>#include<cstdio>#include<cctype>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>#include<set>#include<map>#include<queue>#include<vector>#include<string>#define ll long long#define lson (p<<1)#define rson (p<<1|1)#define lowbit(x) ((x)&(-x))#define siz(x) ((int)(x).size())using namespace std;inline void Rd(int &res){ char c;res=0; while(c=getchar(),c<'0'); do{ res=(res<<1)+(res<<3)+(c^48); }while(c=getchar(),c>='0');}const int M=(int)1e5+5;struct Edge{int to,w,nxt;}Edge[M<<1];struct Node{int x,d;}Q[M<<1],fa[M];int Head[M],tot,n,tot_time,id[M],sz,mx_id;ll d,dep[M<<1],Max,Min=-1,mx,sum[M<<1];bool InCir[M];inline void Addedge(int a,int b,int c){ Edge[++tot].to=b;Edge[tot].w=c;Edge[tot].nxt=Head[a];Head[a]=tot; Edge[++tot].to=a;Edge[tot].w=c;Edge[tot].nxt=Head[b];Head[b]=tot;}void dfs_cir(int x){//抽环 id[x]=++tot_time;//时间戳 for(int i=Head[x];~i;i=Edge[i].nxt){ int to=Edge[i].to; if(!id[to]){ fa[to].x=x; fa[to].d=Edge[i].w; dfs_cir(to); }else if(id[to]>id[x]){ int t=to; while(t!=x){ InCir[t]=true; Q[++sz].x=t; Q[sz].d=fa[t].d; t=fa[t].x; } InCir[x]=true; Q[++sz].x=x; Q[sz].d=Edge[i].w; return; } }}void dfs_tree(int x,int f,ll d){ if(d>mx)mx=d,mx_id=x; for(int i=Head[x];~i;i=Edge[i].nxt){ int to=Edge[i].to; if(to==f||InCir[to])continue; dfs_tree(to,x,d+Edge[i].w); }}struct tree{ ll mx1,mx2; int id1,id2; tree(){id1=id2=-1;}};struct SegTree{ tree Tree[M<<3]; int f; tree Up(tree A,tree B){ tree res; if(B.id1==-1||A.mx1>B.mx1){ res.mx1=A.mx1; res.id1=A.id1; if(B.id1==-1||(~A.id2&&A.mx2>B.mx1)){ res.mx2=A.mx2; res.id2=A.id2; }else{ res.mx2=B.mx1; res.id2=B.id1; } }else{ res.mx1=B.mx1; res.id1=B.id1; if(A.id1==-1||(~B.id2&&B.mx2>A.mx1)){ res.mx2=B.mx2; res.id2=B.id2; }else{ res.mx2=A.mx1; res.id2=A.id1; } } return res; } void Build(int L,int R,int p){ if(L==R){ Tree[p].mx1=dep[L]+f*sum[L]; Tree[p].id1=L; return; } int mid=(L+R)>>1; Build(L,mid,lson); Build(mid+1,R,rson); Tree[p]=Up(Tree[lson],Tree[rson]); } tree Query(int L,int R,int l,int r,int p){ if(L==l&&r==R){ return Tree[p]; } int mid=(L+R)>>1; if(r<=mid)return Query(L,mid,l,r,lson); else if(l>mid)return Query(mid+1,R,l,r,rson); else{ tree A=Query(L,mid,l,mid,lson),B=Query(mid+1,R,mid+1,r,rson); return Up(A,B); } }}T[2];int main(){// freopen("foodshop.in","r",stdin);// freopen("foodshop.out","w",stdout); memset(Head,-1,sizeof(Head)); Rd(n); for(int i=1;i<=n;i++){ int a,b,c; Rd(a),Rd(b),Rd(c); Addedge(a,b,c); } dfs_cir(1); for(int i=1;i<=sz;i++){ InCir[Q[i].x]=false; mx=-1;dfs_tree(Q[i].x,0,0); dep[i]=mx; dfs_tree(mx_id,0,0);//找直径 InCir[Q[i].x]=true; if(mx>Max)Max=mx; } for(int i=1;i<=sz;i++)Q[i+sz]=Q[i],dep[i+sz]=dep[i]; for(int i=1;i<=sz<<1;i++)sum[i]=sum[i-1]+Q[i-1].d; T[0].f=1;T[1].f=-1; T[0].Build(1,sz<<1,1); T[1].Build(1,sz<<1,1); for(int i=1;i<=sz;i++){ tree A=T[0].Query(1,sz<<1,i,i+sz-1,1); tree B=T[1].Query(1,sz<<1,i,i+sz-1,1); ll rs; if(A.id1==B.id1)rs=max(A.mx1+B.mx2,A.mx2+B.mx1);//同一点 else rs=A.mx1+B.mx1; if(Min==-1||rs<Min)Min=rs; } printf("%.1f\n",max(Max,Min)/2.0); return 0;}
- NOI2013 快餐店
- [Noi2013]快餐店
- BZOJ3242: [Noi2013]快餐店
- bzoj3242: [Noi2013]快餐店
- 3242: [Noi2013]快餐店
- NOI2013快餐店【图上找环+线段树】
- 【树DP+基环树】[NOI2013]快餐店
- [BZOJ3242][Noi2013]快餐店 && 环套树+线段树
- bzoj3242: [Noi2013]快餐店 树形dp+线段树
- bzoj 3242: [Noi2013]快餐店 dfs&递推
- [BZOJ3242][NOI2013]快餐店-基环树-动态规划
- [BZOJ3242][Noi2013]快餐店(树形dp+线段树)
- [线段树优化 DP] BZOJ 3242 [Noi2013]快餐店
- 3066 快餐店
- NOI2013行纪
- NOI2013 书法家
- 上海快餐店一览
- 《DOS快餐店》笔记
- [dijkstra//vis数组变形输入]昂贵的聘礼
- 基数排序,图解
- leetcode_374. Guess Number Higher or Lower 猜数字是比给定的数字高了还是低了,二分查找法
- redhat命令操作(二)
- win7 git上传本地项目至github
- NOI2013 快餐店
- Linux sqlite3基本命令
- Ubuntu16.04lts使用Anaconda安装tensorflow并配置GPU
- JavaOO总结
- Unity3D插件之Highlighting System:物件高亮插件
- 关于javaee编程中的习惯以及解决思路总结
- 个人黑历史1
- 字符串之间的空格
- LeetCode(122) Best Time to Buy and Sell Stock II