倍增法-lca codevs 1036 商务旅行

codevs 1036 商务旅行

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 钻石 Diamond
题目描述  Description

某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间。

假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任意两个城镇之间如果有直连道路,在他们之间行驶需要花费单位时间。该国公路网络发达,从首都出发能到达任意一个城镇,并且公路网络不会存在环。

你的任务是帮助该商人计算一下他的最短旅行时间。

输入描述  Input Description

输入文件中的第一行有一个整数N,1<=n<=30 000,为城镇的数目。下面N-1行,每行由两个整数a 和b (1<=ab<=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 #define N 60100
 2 #include<iostream>
 3 using namespace std;
 4 #include<cstdio>
 5 #include<cstring>
 6 #define C 20
 7 typedef long long ll;
 8 int ance[N>>1][C],dis[N>>1],n,a,b,m;
 9 ll sum=0;
10 struct Edge{
11     int v,last;
12 }edge[N<<1];
13 int deep[N>>1],head[N>>1],t=0;
14 void add_edge(int u,int v)
15 {
16     ++t;
17     edge[t].v=v;
18     edge[t].last=head[u];
19     head[u]=t;
20 }
21 void input()
22 {
23     scanf("%d",&n);
24     for(int i=1;i<n;++i)
25     {
26         scanf("%d%d",&a,&b);
27         add_edge(a,b);
28         add_edge(b,a);/*注意树上建边必须是双向的*/
29     }
30 }
31 void dfs(int k)
32 {
33     for(int l=head[k];l;l=edge[l].last)
34     {
35         if(!deep[edge[l].v])
36         {
37             dis[edge[l].v]=dis[k]+1;
38             ance[edge[l].v][0]=k;
39             deep[edge[l].v]=deep[k]+1;
40             dfs(edge[l].v);
41         }
42     }
43 }
44 void chuli_ance()
45 {
46     for(int i=1;(1<<i)<=n;++i)
47       for(int j=1;j<=n;++j)
48       ance[j][i]=ance[ance[j][i-1]][i-1];
49 }
50 int lca(int a,int b)
51 {
52     int i,j;
53     if(deep[a]<deep[b]) swap(a,b);
54     for(i=0;(1<<i)<=deep[a];++i);
55       i--;
56     for(j=i;j>=0;--j)
57       if(deep[a]-(1<<j)>=deep[b])
58       a=ance[a][j];
59     if(a==b) return a;
60     for(j=i;j>=0;--j)
61     {
62         if(ance[a][j]!=-1&&ance[a][j]!=ance[b][j])
63         {
64             a=ance[a][j];
65             b=ance[b][j];
66         }
67     }
68     return ance[a][0];
69 }
70 int main()
71 {
72     input();
73     deep[1]=1;
74     dis[1]=0;
75     memset(ance,-1,sizeof(ance));
76     dfs(1);
77     chuli_ance();
78     scanf("%d",&m);
79     scanf("%d",&a);
80     int sta=a;
81     for(int i=2;i<=m;++i)
82     {
83         scanf("%d",&b);
84         int zuxian=lca(a,b);
85         sum+=(dis[a]+dis[b]-2*dis[zuxian]);
86         a=b;
87     }
88     sum+=dis[sta];
89     cout<<sum<<endl;
90     return 0;
91 }

 

转载于:https://www.cnblogs.com/c1299401227/p/5595029.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值