水一发题解。

我只是想存一下树剖LCA的代码......

以洛谷上的这个模板为例:P3379 【模板】最近公共祖先(LCA)

1.朴素LCA

就像做模拟题一样,先dfs找到基本信息:每个节点的父亲、深度。

把深的节点先往上跳。

深度相同了之后,一起往上跳。

最后跳到一起了就是LCA了。

预处理:O(n)

每次查询:O(n)

 

2.倍增LCA

朴素LCA的一种优化。

一点一点跳,显然太慢了。

如果要跳x次,可以把x转换为二进制。

每一位都是1或0,也就是跳或者不跳。

在第i位,如果跳,就向上跳2(i-1)次。

至于跳或者不跳,判断很简单。

如果跳了之后还没在一起,就跳。

预处理:算出每个点上跳2n次后的位置。(已知上跳20次的位置就是它的父亲)O(nlogn)

每次询问:O(logn)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 int n,m,s;
 7 int hd[500005],nx[1000005],to[1000005],cnt;
 8 
 9 void add(int af,int at)
10 {
11     to[++cnt]=at;
12     nx[cnt]=hd[af];
13     hd[af]=cnt;
14 }
15 
16 int d[500005],f[500005][25];
17 
18 void pre(int p,int fa)
19 {
20     f[p][0]=fa;
21     d[p]=d[fa]+1;
22     for(int i=hd[p];i;i=nx[i])
23     {
24         if(to[i]!=fa)pre(to[i],p);
25     }
26 }
27 
28 int lca(int x,int y)
29 {
30     if(d[x]<d[y])swap(x,y);
31     for(int i=20;i>=0;i--)
32     {
33         if(d[f[x][i]]>=d[y])x=f[x][i];
34     }
35     if(x==y)return x;
36     for(int i=20;i>=0;i--)
37     {
38         if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
39     }
40     return f[x][0];
41 }
42 
43 int main()
44 {
45     scanf("%d%d%d",&n,&m,&s);
46     for(int i=1;i<n;i++)
47     {
48         int aa,bb;
49         scanf("%d%d",&aa,&bb);
50         add(aa,bb);
51         add(bb,aa);
52     }
53     pre(s,0);
54     for(int i=1;i<=20;i++)
55     {
56         for(int j=1;j<=n;j++)
57         {
58             f[j][i]=f[f[j][i-1]][i-1];
59         }
60     }
61     for(int i=1;i<=m;i++)
62     {
63         int x,y;
64         scanf("%d%d",&x,&y);
65         printf("%d\n",lca(x,y));
66     }
67     return 0;
68 }
倍增LCA

相关文章: