【codevs1036】商务旅行 LCA 倍增

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


题解:
  这道题主要是用来巩固lca和倍增算法的,是一道模板题。一个简单的树上倍增,找最近公共祖先。注意局部ans的细节处理,不是对整体相乘,而是对当前步数乘以2。然后借鉴了网上的博客的另一种计算方法:用depth来计算。写第一遍时又把倍增递推写错了。。注意数组开两倍。。
我的方法:
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define maxn 30005
 6 using namespace std;
 7 int n,m,city[maxn],ans,fist;
 8 int tot,he[maxn],to[maxn*2],ne[maxn*2];
 9 bool flag[maxn];
10 int f[14][maxn],depth[maxn];
11 void add(int a,int b)
12 {
13     tot++;to[tot]=b;ne[tot]=he[a];he[a]=tot;
14 }
15 void build (int x)
16 {
17     for (int i=he[x];i;i=ne[i])
18     if (!flag[to[i]]){
19         flag[to[i]]=true;
20         depth[to[i]]=depth[x]+1;
21         f[0][to[i]]=x;
22         build(to[i]);
23     }    
24 }
25 void bz()
26 {
27     for (int i=1;i<=12;i++) 
28       for (int j=1;j<=n;j++)
29          f[i][j]=f[i-1][f[i-1][j]];
30 }
31 int lca(int a,int b)
32 {
33     int mi=0;
34     if (depth[a]<depth[b]) swap(a,b);
35     int derta=depth[a]-depth[b];
36     for (int i=0;i<=12;i++)
37     {
38         if (1<<i & derta)
39         {
40             mi+=(1<<i);
41             a=f[i][a];    
42         }
43     }
44     if (a==b) return mi;
45     for (int i=12;i>=0;i--)
46     {
47         if (f[i][a]!=f[i][b]) {
48             a=f[i][a];
49             b=f[i][b];
50             int p=(1<<i);p*=2;//not mi+=(1<<i);mi*=2;
51             mi+=p;
52         }
53     }
54     a=f[0][a];b=f[0][b];
55     mi+=2;
56     return mi;
57 }
58 int main()
59 {
60     freopen("codevs1036.in","r",stdin);
61     cin>>n;
62     for (int i=1;i<n;i++)
63     {
64         int x,y;
65         scanf("%d%d",&x,&y);
66         add(x,y);
67         add(y,x);
68     }
69     flag[1]=true;
70     depth[1]=1;
71     build(1);
72     bz();
73     cin>>m;cin>>fist;
74     for (int i=1;i<m;i++)
75     {
76         int x;scanf("%d",&x);
77         int y=lca(fist,x);
78         ans+=y;
79         fist=x;
80     }
81     cout<<ans<<endl;
82     return 0;
83 }
View Code

借鉴方法:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define maxn 30005
 6 using namespace std;
 7 int n,m,city[maxn],ans,fist;
 8 int tot,he[maxn],to[maxn*2],ne[maxn*2];
 9 bool flag[maxn];
10 int f[14][maxn],depth[maxn];
11 void add(int a,int b)
12 {
13     tot++;to[tot]=b;ne[tot]=he[a];he[a]=tot;
14 }
15 void build (int x)
16 {
17     for (int i=he[x];i;i=ne[i])
18     if (!flag[to[i]]){
19         flag[to[i]]=true;
20         depth[to[i]]=depth[x]+1;
21         f[0][to[i]]=x;
22         build(to[i]);
23     }    
24 }
25 void bz()
26 {
27     for (int i=1;i<=12;i++) 
28       for (int j=1;j<=n;j++)
29          f[i][j]=f[i-1][f[i-1][j]];
30 }
31 int lca(int a,int b)
32 {
33     //int mi=0;
34     if (depth[a]<depth[b]) swap(a,b);
35     int derta=depth[a]-depth[b];
36     for (int i=0;i<=12;i++)
37     {
38         if (1<<i & derta)
39         {
40             //mi+=(1<<i);
41             a=f[i][a];    
42         }
43     }
44     if (a==b) return a;
45     for (int i=12;i>=0;i--)
46     {
47         if (f[i][a]==f[i][b]) continue;
48         else {
49             a=f[i][a];
50             b=f[i][b];
51         //    mi+=(1<<i);mi=(mi<<1);
52         }
53     }
54 //    a=f[0][a];b=f[0][b];
55     //mi+=2;
56     return f[0][a];
57 }
58 int main()
59 {
60     freopen("codevs1036.in","r",stdin);
61     cin>>n;
62     for (int i=1;i<n;i++)
63     {
64         int x,y;
65         scanf("%d%d",&x,&y);
66         add(x,y);
67         add(y,x);
68     }
69     flag[1]=true;
70     depth[1]=1;
71     build(1);
72     bz();
73     cin>>m;cin>>fist;
74     for (int i=1;i<m;i++)
75     {
76         int x;scanf("%d",&x);
77         ans+=depth[fist]+depth[x]-2*depth[lca(fist,x)];
78         fist=x;
79     }
80     cout<<ans<<endl;
81     return 0;
82 }
View Code
原文地址:https://www.cnblogs.com/lx0319/p/5971074.html