[TJOI2017]城市(树的直径)
[TJOI2017]城市
题目描述
从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作。这个地区一共有ri座城市,《-1条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达,但是通过一条高速公路需要收取一定的交通费用。小明对这个地区深入研究后,觉得这个地区的交通费用太贵。小明想彻底改造这个地区,但是由于上司给他的资源有限,因而小明现在只能对一条高速公路进行改造,改造的方式就是去掉一条高速公路,并且重新修建一条一样的高速公路(即交通费用一样),使得这个地区的两个城市之间的最大交通费用最小(即使得交通费用最大的两座城市之间的交通费用最小),并且保证修建完之后任意两座城市相互可达。如果你是小明,你怎么解决这个问题?
输入输出格式
输入格式:
输入数据的第一行为一个整数n,代表城市个数。
接下来的n - 1行分别代表了最初的n-1条公路情况。每一行都有三个整数u,v,d。u,v代表这条公路的两端城市标号,d代表这条公路的交通费用。
1 <= u,v <= n,1<= d <= 2000
输出格式:
输出数据仅有一行,一个整数,表示进行了最优的改造之后,该地区两城市 之间最大交通费用。
输入输出样例
输入样例#1:
5
1 2 1
2 3 2
3 4 3
4 5 4
输出样例#1:
7
说明
对于30%的数据,1<=n<500
对于100%的数据,1<=n<=5000
感觉这道题挺难码的.....
看了数据和时间之后大概能猜出是\(O(n^2)\)的算法。
于是我们直接枚举断开哪条边,显然我们可以直接断开直径上的边,否则答案不会变小。(直径的所有边仍然在一条联通块中)。
然后整张图会变成两棵树,我最开始的想法,直接在原直径上找两个中点,然后将这两个中点连接起来。但是后来发现这样连接不一定最优,(自己yy一下,连接到一个非直径上的点,这个点连接到原直径一段的中点,这样会比上述连接方法更优)。
在考虑到能够承受\(O(n^2)\)的复杂度,所以我们在两个联通块中跑树的直径,然后找中点连接。
注意:中点有可能在边上,最多需要考虑4个点相连。
#include<bits/stdc++.h>
#define Max(a,b) (a)>(b)?(a):(b);
using namespace std;
int read()
{
int x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*w;
}
const int N=5010;
int n,cnt,l,r,x,y,z,sum1,sum2,ans1,ans2,ans3,ans4,ans,a1,a2,b1,b2;
int head[N],dis[N],len[N],ff[N],fa[N];
bool vis[N];
struct node{
int v,to,next;
}edge[2*N];
void add(int x,int y,int z)
{
cnt++;edge[cnt].v=z;edge[cnt].to=y;edge[cnt].next=head[x];head[x]=cnt;
}
void dfs(int k,int father)
{
for(int i=head[k];i;i=edge[i].next)
{
int v=edge[i].to;if(v==father)continue;
dis[v]=dis[k]+edge[i].v;fa[v]=k;dfs(v,k);
}
}
void dfs1(int k,int fa,int top)
{
for(int i=head[k];i;i=edge[i].next)
{
int v=edge[i].to;if(v==fa||v==top)continue;
dis[v]=dis[k]+edge[i].v;ff[v]=k;dfs1(v,k,top);
}
}
int cut(int x,int y)
{
ans1=ans2=0;
int qwe,ll=0,rr=0;
for(int i=1;i<=n;i++) dis[i]=ff[i]=0;
dfs1(x,0,y);
for(int i=1;i<=n;i++) if(dis[i]>dis[ll]) ll=i;
for(int i=1;i<=n;i++) dis[i]=ff[i]=0;
dfs1(ll,0,y);
for(int i=1;i<=n;i++) if(dis[i]>dis[rr]) rr=i;
qwe=rr;
while(qwe!=ll)
{
int f=ff[qwe];
if(dis[f]*1.0==dis[rr]*1.0/2) {a1=a2=f;break;}
else if(dis[f]*1.0<dis[rr]*1.0/2){a1=f;a2=qwe;break;}
qwe=f;
}
ans1=Max(dis[a1],dis[rr]-dis[a1]);
ans2=Max(dis[a2],dis[rr]-dis[a2]);
sum1=dis[rr];
ll=rr=0;
for(int i=1;i<=n;i++) dis[i]=ff[i]=0;
dfs1(y,0,x);
for(int i=1;i<=n;i++) if(dis[i]>dis[ll]) ll=i;
for(int i=1;i<=n;i++) dis[i]=ff[i]=0;
dfs1(ll,0,x);
for(int i=1;i<=n;i++) if(dis[i]>dis[rr]) rr=i;
qwe=rr;
while(qwe!=ll)
{
int f=ff[qwe];
if(dis[f]*1.0==dis[rr]*1.0/2) {b1=b2=f;break;}
else if(dis[f]*1.0<dis[rr]*1.0/2){b1=f;b2=qwe;break;}
qwe=f;
}
ans3=Max(dis[b1],dis[rr]-dis[b1]);
ans4=Max(dis[b2],dis[rr]-dis[b2]);
sum2=dis[rr];
int asd=min(min(ans1+ans3,ans1+ans4),min(ans2+ans3,ans2+ans4));
return Max(asd+len[x]-len[y],max(sum1,sum2));
}
int main()
{
n=read();
for(int i=1;i<n;i++)
{
x=read();y=read();z=read();
add(x,y,z);add(y,x,z);
}
dfs(1,0);
for(int i=1;i<=n;i++)
if(dis[i]>dis[l]) l=i;
for(int i=1;i<=n;i++)dis[i]=fa[i]=0;
dfs(l,0);
for(int i=1;i<=n;i++)
if(dis[i]>dis[r]) r=i;
int qwe=r;ans=2000000000;
for(int i=1;i<=n;i++) len[i]=dis[i];
while(fa[qwe])
{
ans=min(ans,cut(qwe,fa[qwe]));
qwe=fa[qwe];
}
cout<<ans;
}