【树DP+基环树】[NOI2013]快餐店
题目描述
小 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,然后我们来考虑基环树。
我们可以把基环树看做一个环+一个森林,其中每棵树的根就是这棵树和环的公共点。
显然环上总有一条边一定不会被走到,我们枚举环上的边,删掉它,然后求直径,时间复杂度
我们能不能快速求直径呢?
我们考虑直径有哪些情况:
- 在树的内部
这个我们直接预处理,然后取一个最大值,记作mx ,因为删除环上的边对于这种情况没有影响。 - 经过了环上的边
这种情况是我们重点需要解决的问题。
接下来,我们来讨论这个问题:
注意,为方便表述,以下的编号为破环为链后数组的下标
首先,破环为链(即将这个环的序列再复制一遍),成为一个序列,每次删边之后就对应序列的一个区间,我们令
其实
那么最长链就为
这里有一个问题,假设
我们分类讨论一下
有三个点
- 如果两个最大值不在同一处出现,假设
Di+sumi 的最大值出现在j 这个位置,而且Di−sumi 的最大值出现在k 这个位置,那么Dk−sumk>Dj−sumjDk>Dj−sumj+sumk
因为所以sumk>sumj 与假设相悖Dk>DjDk+sumk>Dj+sumj - 如果两个最大值在同一处出现,假设出现在
j ,那么我们假设Di−sumi 的最大值出现在k ,显然根据上一个证明,Dk+sumk>Di+sumi ,而Dj+sumj+Dk−sumk<Dj−sumj+Dk+sumk ,所以这种情况不会被算进最优解。
所以,我们可以放心地使用这种方法。时间复杂度
代码
#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);
}