LCA 四种解法之一: 倍增 以及例题

hdu-2586 http://acm.hdu.edu.cn/showproblem.php?pid=2586

//题意:一村庄中有n栋房屋, 两房屋之间只有一条路径, 且村庄是联通的。问m次查询两房屋之间的距离,输出每次查询的答案。n(2 <= n <= 40000) m(1 <= m <= 200)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int MAXN = 4e4+5;
 7 
 8 struct node {// 链式向前星  具体原理见博客https://blog.csdn.net/acdreamers/article/details/16902023
 9     int u, v, w, next;
10 } edge[2*MAXN];
11 
12 int n, m;
13 int num = 1;// 结点的编号
14 int head[2*MAXN];// 用来映射某值的结点编号
15 int deep[MAXN];// 某点的深度(树)
16 int f[MAXN][21];// f[i][j] 表示i的第2^j祖先
17 int g[MAXN];// 存某点到树根的距离
18 
19 void add_edge(int x, int y, int z) {// 建树 
20     edge[num].u = x;
21     edge[num].v = y;
22     edge[num].w = z;
23     edge[num].next = head[x];
24     head[x] = num++;
25 }
26 
27 void dfs(int cur) {// 算出每一结点的父亲结点编号、深度、和到树根的距离
28     for(int i = head[cur]; i != -1; i = edge[i].next) {
29         if(deep[edge[i].v] == 0) {
30             deep[edge[i].v] = deep[cur] + 1;
31             f[edge[i].v][0] = cur;
32             g[edge[i].v] = g[cur] + edge[i].w;
33             dfs(edge[i].v);
34         }
35     }
36 }
37 
38 void PRE() {// 倍增求f[i][j]
39     for(int j = 1; j <= 19; ++j) {
40         for(int i = 1; i <= n; ++i) {
41             f[i][j] = f[f[i][j-1]][j-1];
42         }
43     }
44 }
45 
46 int LCA(int x, int y) {// 倍增思想
47     if(deep[x] < deep[y]) swap(x, y);
48     for(int i = 19; i >= 0; --i) {
49         if(deep[f[x][i]] >= deep[y]) {
50             x = f[x][i];
51         }
52     }
53     if(x == y) return x;
54     for(int i = 19; i >= 0; --i) {
55         if(f[x][i] != f[y][i]) {
56             x = f[x][i];
57             y = f[y][i];
58         }
59     }
60     return f[x][0];
61 }
62 
63 int main() {
64     int T;
65     scanf("%d", &T);
66     while(T--) {
67         memset(head, -1, sizeof(head));
68         memset(deep, 0, sizeof(deep));
69         memset(f, 0, sizeof(f));
70         memset(g, 0, sizeof(g));
71 
72         scanf("%d%d", &n, &m);
73         int s, e, v;
74         for(int i = 0; i != n-1; ++i) {
75             scanf("%d%d%d", &s, &e, &v);
76             add_edge(s, e, v);
77             add_edge(e, s, v);
78         }
79         deep[1] = 1;
80         dfs(1);
81         PRE();
82         for(int i = 0; i != m; ++i) {
83             scanf("%d%d", &s, &e);
84             printf("%d\n", g[s]+g[e]-2*g[LCA(s, e)]);// 画图即可理解
85         }
86     }
87     return 0;
88 }

 //poj-1986 和上面一题一模一样,没有任何修改。

// 这题输入很奇葩,(我用)用scanf就是wa,然后学了下别人的输入就ac了,很奇怪😵。。。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int MAXN = 1e5+5;
 7 inline int read() {
 8     char c = getchar(); int x = 0, f = 1;
 9     while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
10     while(c >= '0' && c <= '9') { x = x*10 + c - '0'; c = getchar(); }
11     return x*f;
12 }
13 // 这中间代码和上面一题一模一样。。。 主函数里面其实也是。输入很奇葩,改成这样就对了。
14 int main() {
15     n = read(), m = read();
16     memset(head, -1, sizeof(head));
17     memset(f, 0, sizeof(f));
18     memset(g, 0, sizeof(g));
19     memset(deep, 0, sizeof(deep));
20 
21     int s, e, v;
22     for(int i = 0; i != m; ++i) {
23         s = read(), e = read(), v = read();
24         add_edge(s, e, v);
25         add_edge(e, s, v);
26     }
27     deep[1] = 1;
28     dfs(1);
29     PRE();
30     m = read();
31     for(int i = 0; i != m; ++i) {
32         s = read(), e = read();
33         printf("%d\n", g[s]+g[e]-2*g[LCA(s, e)]);
34     }
35     return 0;
36 }

 

posted @ 2019-09-28 19:44  pupil337  阅读(217)  评论(0)    收藏  举报