【问题描述】 | ||
Chris家的电话铃响起了,里面传出了Chris的老师焦急的声音:“喂,是Chris的家长吗?你们的孩子又没来上课,不想参加考试了吗?”一听说要考试,Chris的父母就心急如焚,他们决定在尽量短的时间内找到Chris。他们告诉Chris的老师:“根据以往的经验,Chris现在必然躲在朋友Shermie或Yashiro家里偷玩《拳皇》游戏。现在,我们就从家出发去找Chris,一但找到,我们立刻给您打电话。”说完砰的一声把电话挂了。 Chris居住的城市由N个居住点和若干条连接居住点的双向街道组成,经过街道x需花费Tx分钟。可以保证,任两个居住点间有且仅有一条通路。Chris家在点C,Shermie和Yashiro分别住在点A和点B。Chris的老师和Chris的父母都有城市地图,但Chris的父母知道点A、B、C的具体位置而Chris的老师不知。 为了尽快找到Chris,Chris的父母会遵守以下两条规则: 如果A距离C比B距离C近,那么Chris的父母先去Shermie家寻找Chris,如果找不到,Chris的父母再去Yashiro家;反之亦然。 Chris的父母总沿着两点间唯一的通路行走。 显然,Chris的老师知道Chris的父母在寻找Chris的过程中会遵守以上两条规则,但由于他并不知道A,B,C的具体位置,所以现在他希望你告诉他,最坏情况下Chris的父母要耗费多长时间才能找到Chris? 例如上图,这座城市由4个居住点和3条街道组成,经过每条街道均需花费1分钟时间。假设Chris住在点C,Shermie住在点A,Yashiro住在点B,因为C到B的距离小于C到A的距离,所以Chiris的父母会先去Yashiro家寻找Chris,一旦找不到,再去Shermie家寻找。这样,最坏情况下Chris的父母需要花费4分钟的时间才能找到Chris。 |
【输入格式】 | |||
第一行是两个整数N(3<=N<=200000)和M,分别表示居住点总数和街道总数。以下M行,每行给出一条街道的信息。第i+1行包含整数Ui、Vi、Ti(1<=Ui,Vi<=N,1<=Ti<= 1000000000),表示街道i连接居住点Ui和Vi,并且经过街道i需花费Ti分钟。街道信息不会重复给出。 |
【输出格式】 | |||
仅包含整数T,即最坏情况下Chris的父母需要花费T分钟才能找到Chris。 |
【输入样例】 | |||
4 3 1 2 1 2 3 1 3 4 1 |
【输出样例】 | |||
4 |
【来源】 | |||
NOI2003 |
如图,题目可以表示为从C先到B再经过O到A(设OB<OA),求最短距离(忽略其他边)
遇到这种题可以先猜一个结论再证明它的正确性
结论:A,B为树的直径且BC最长时即为答案,即ans=AB(设为base)+(CO+OB)(设为add)
参考证明思路:
反证法
先证明一个引理:A,B不在直径端点上时,所得的最大ans一定小于等于在A,B上时的ans
在证明另一个引理:C不在结论中的C点上时,所得的最大ans也一定小于等于原ans
综合两个引理,结论得证
【代码】:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
const int maxn=200005;
using namespace std;
struct edge{
int to,next,l;
}e[maxn<<1];
int first[maxn],np=0,n,m;
int nt=0,id[maxn],l[maxn],r[maxn];
void scan(int& in){
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
in=0;
while(isdigit(ch)) in=in*10+ch-'0',ch=getchar();
}
void addedge(int u,int v,int l){
e[++np]=(edge){v,first[u],l};
first[u]=np;
}
void init(){
int x,y,l;
scan(n),scan(m);
for(int i=1;i<n;i++){
scan(x),scan(y),scan(l);
addedge(x,y,l);
addedge(y,x,l);
}
}
long long dis[maxn];
int fa[maxn];
void dfs(int i,int f,long long dist,int& o){
fa[i]=f;dis[i]=dist;
if(dist>dis[o]) o=i;
for(int p=first[i];p;p=e[p].next){
int j=e[p].to;
if(j==f) continue;
dfs(j,i,dist+e[p].l,o);
}
}
bool vis[maxn];
void dfs(int i,long long dist,long long& mx){
vis[i]=true;
if(dist>mx) mx=dist;
for(int p=first[i];p;p=e[p].next){
int j=e[p].to;
if(vis[j]) continue;
dfs(j,dist+e[p].l,mx);
}
}
int main(){
init();//建立数据结构
int a,b;
dfs(1,0,0,a=1);//寻找直径
dfs(a,0,0,b=a);
long long base=dis[b],add=0,mid=dis[b]>>1;
for(int p=b;p;p=fa[p]) vis[p]=true;//设置为已经访问
for(int p=b;p;p=fa[p]){//寻找最大add
long long t=0;
dfs(p,0,t);
if(dis[p]>mid) t+=base-dis[p];//保证OB<OA
else t+=dis[p];
add=max(add,t);
}
printf("%lld\n",base+add);
return 0;
}