题目链接:http://codeforces.com/contest/832/problem/D
题目大意:在一个无向图上,给出三个点,以其中一个点为终点,另外两个点为起点,请问如何安排起点和终点可以使得从两个起点走最短路到终点所共同经过的路径的长度最长?
解题思路:任取一个点作为根建立一棵树(为什么可以建成一棵树?它不会有环吗?请看题目中的这一句:“The underground has n stations connected with n - 1 routes so that each route connects two stations, and it is possible to reach every station from any other.”),要求树中任意两点x,y的距离,其实只要先求出两点的公共祖先u,然后其距离便是 |depth[x]+depth[y]-2*depth[u]| 。对于一组确定的终点和起点,其共同路径长度就是 (dis(s1,t) + dis(s2,t) - dis(s1,s2))/2+1 (不理解的话稍微画一下图,至于为什么+1?根节点也要算哦亲)。具体请看代码(模板来自《挑战》)。
AC代码:
1 #include <cstdio> 2 #include <vector> 3 #include <cmath> 4 using namespace std; 5 const int maxn=1e5+5; 6 vector<int> G[maxn]; 7 int log_max_v; 8 int parent[25][maxn]; 9 int depth[maxn],s[5]; 10 int n,q; 11 void dfs(int v,int p,int d){ 12 parent[0][v]=p; 13 depth[v]=d; 14 for(int i=0;i<G[v].size();i++){ 15 if(G[v][i]!=p) dfs(G[v][i],v,d+1); 16 } 17 } 18 void init(){ 19 dfs(1,-1,0); 20 for(int k=0;k+1<20;k++){ 21 for(int v=1;v<=n;v++){ 22 if(parent[k][v]<0) parent[k+1][v]=-1; 23 else parent[k+1][v]=parent[k][parent[k][v]]; 24 } 25 } 26 } 27 int lca(int u,int v){ 28 if(depth[u]>depth[v]) swap(u,v); 29 for(int k=0;k<20;k++){ 30 if((depth[v]-depth[u])>>k&1){ 31 v=parent[k][v]; 32 } 33 } 34 if(u==v) return u; 35 for(int k=20;k>=0;k--){ 36 if(parent[k][u]!=parent[k][v]){ 37 u=parent[k][u]; 38 v=parent[k][v]; 39 } 40 } 41 return parent[0][u]; 42 } 43 int dis(int a,int b){ 44 int u=lca(a,b); 45 return abs(depth[a]+depth[b]-depth[u]*2); 46 } 47 int main() 48 { 49 scanf("%d%d",&n,&q); 50 int p; 51 for(int i=2;i<=n;i++){ 52 scanf("%d",&p); 53 G[i].push_back(p); 54 G[p].push_back(i); 55 } 56 init(); 57 while(q--){ 58 for(int i=0;i<3;i++) 59 scanf("%d",&s[i]); 60 61 int ans=0; 62 for(int i=0;i<3;i++){ 63 int a=(i+1)%3,b=(i+2)%3; 64 int temp=(dis(s[a],s[i])+dis(s[b],s[i])-dis(s[a],s[b]))/2; 65 if(temp>ans) ans=temp; 66 } 67 printf("%d ",ans+1); 68 } 69 70 return 0; 71 }