Codevs 1036 商务旅行
时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond
题目描述:
某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间。
假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任意两个城镇之间如果有直连道路,在他们之间行驶需要花费单位时间。该国公路网络发达,从首都出发能到达任意一个城镇,并且公路网络不会存在环。
你的任务是帮助该商人计算一下他的最短旅行时间。
输入描述 Input Description
输入文件中的第一行有一个整数N,1<=n<=30 000,为城镇的数目。下面N-1行,每行由两个整数a 和b (1<=a, b<=n; a<>b)组成,表示城镇a和城镇b有公路连接。在第N+1行为一个整数M,下面的M行,每行有该商人需要顺次经过的各城镇编号。
输出描述 Output Description
在输出文件中输出该商人旅行的最短时间。
样例输入 Sample Input
5
1 2
1 5
3 5
4 5
4
1
3
2
5
样例输出 Sample Output
7
1 #include<cstring>
2 #include<cstdio>
3 #include<algorithm>
4 #include<iostream>
5 using namespace std;
6 #define maxn 30010
7 #define S 16
8 int n,m,a[30010],deep[30010],fa[maxn][S+10],ans,p1,p2,head[30010],num;
9
10 struct node{
11 int u,v,pre;
12 }e[maxn*2];
13 void dfs(int now,int from,int deepth)
14 {
15 fa[now][0]=from;
16 deep[now]=deepth;
17 for(int i=head[now];i;i=e[i].pre)
18 if(e[i].v!=from)
19 dfs(e[i].v,now,deepth+1);
20 }
21 void get_fa()
22 {
23 for(int j=1;j<=S;j++)
24 for(int i=1;i<=n;i++)
25 fa[i][j]=fa[fa[i][j-1]][j-1];
26 }
27 int LCA(int a,int b)
28 {
29 if(deep[a]<deep[b])swap(a,b);
30 for(int j=S;j>=0;j--)
31 if((deep[a]-(1<<j))>=deep[b])
32 a=fa[a][j];
33 if(a==b)return a;
34 for(int i=S;i>=0;i--)
35 if(fa[a][i]!=fa[b][i])
36 {
37 a=fa[a][i];
38 b=fa[b][i];
39 }
40 return fa[a][0];
41 }
42 void add_egre(int from,int to)
43 {
44 num++;
45 e[num].u=from;
46 e[num].v=to;
47 e[num].pre=head[from];
48 head[from]=num;
49 }
50 int main()
51 {
52 scanf("%d",&n);
53 for(int i=1,x,y;i<n;i++)
54 {
55 scanf("%d%d",&x,&y);
56 add_egre(x,y);add_egre(y,x);
57 }
58 scanf("%d%d",&m,&p1);
59 memset(deep,0,sizeof(deep));
60 dfs(1,1,0);
61 get_fa();
62 for(int i=1;i<=m-1;i++)
63 {
64 scanf("%d",&p2);
65 int k=LCA(p1,p2);
66 ans+=deep[p1]+deep[p2]-2*deep[k];// deep 该点的深度
67 p1=p2;
68 }
69 printf("%d\n",ans);
70 return 0;
71 }
思路:树上任意两点间的最短距离等于deep[p1]+deep[p2]-2*deep[p1,p2的最近公共祖先]