【NOI2003】逃学的小孩

【问题描述】  
 
  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;
}


猜你喜欢

转载自blog.csdn.net/hi_ker/article/details/79777734