[CodeVS2370] 小机房的树 (LCA, 树链剖分, LCT)

Description

  小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上。有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力。
  已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计一个程序来找到这条路,要求你告诉他们最少需要花费多少精力

Input

  第一行一个n,接下来n-1行每一行有三个整数u,v, c 。表示节点 u 爬到节点 v 需要花费 c 的精力。
  第n+1行有一个整数m表示有m次询问。接下来m行每一行有两个整数 u ,v 表示两只虫子所在的节点

Output

  一共有m行,每一行一个整数,表示对于该次询问所得出的最短距离。

Sample Input

3
1 0 1
2 0 1
3
1 0
2 0
1 2

Sample Output

1
1
2

HINT

  1<=n<=50000, 1<=m<=75000, 0<=c<=1000

Source

Solution

  倍增求LCA, $O((n + m)logn)$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 struct edge
 4 {
 5     int v, w, nxt;
 6 }e[100005];
 7 int fst[50005], q[50005], front, back;
 8 int fa[17][50005], dis[50005], dep[50005];
 9 
10 void addedge(int i, int u, int v, int w)
11 {
12     e[i] = (edge){v, w, fst[u]}, fst[u] = i;
13 }
14 
15 int LCA(int u, int v)
16 {
17     if(dep[u] > dep[v]) swap(u, v);
18     for(int i = 16; ~i; i--)
19         if(dep[fa[i][v]] >= dep[u])
20             v = fa[i][v];
21     if(u == v) return u;
22     for(int i = 16; ~i; i--)
23         if(fa[i][u] != fa[i][v])
24             u = fa[i][u], v = fa[i][v];
25     return fa[0][u];
26 }
27 
28 int main()
29 {
30     int n, m, u, v, w;
31     cin >> n;
32     for(int i = 1; i < n; i++)
33     {
34         cin >> u >> v >> w;
35         addedge(i << 1, ++u, ++v, w);
36         addedge(i << 1 | 1, v, u, w);
37     }
38     q[++back] = 1, dep[1] = fa[0][1] = 1;
39     while(front != back)
40     {
41         u = q[++front];
42         for(int i = fst[u]; i; i = e[i].nxt)
43             if(e[i].v != fa[0][u])
44             {
45                 q[++back] = e[i].v;
46                 dep[e[i].v] = dep[u] + 1;
47                 fa[0][e[i].v] = u;
48                 dis[e[i].v] = dis[u] + e[i].w;
49             }
50     }
51     for(int i = 1; i <= 16; i++)
52         for(int j = 1; j <= n; j++)
53             fa[i][j] = fa[i - 1][fa[i - 1][j]];
54     cin >> m;
55     while(m--)
56     {
57         cin >> u >> v, u++, v++;
58         cout << dis[u] + dis[v] - 2 * dis[LCA(u, v)] << endl;
59     }
60     return 0;
61 }
View Code

  Tarjan求LCA, $O(n + m)$(写法较鬼畜)

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 struct edge
 4 {
 5     int v, w, nxt;
 6 }e[100005];
 7 struct query
 8 {
 9     int u, v, nxt;
10 }q[150005];
11 int efst[50005], qfst[75005], fa[50005], lca[75005], dis[50005];
12 bool vis[50005];
13 
14 void addedge(int i, int u, int v, int w)
15 {
16     e[i] = (edge){v, w, efst[u]}, efst[u] = i;
17 }
18 
19 void addquery(int i, int u, int v)
20 {
21     q[i] = (query){u, v, qfst[u]}, qfst[u] = i;
22 }
23 
24 int get_dis(int i)
25 {
26     return dis[q[i << 1].u] + dis[q[i << 1].v] - 2 * dis[lca[i]];
27 }
28 
29 int getfa(int x)
30 {
31     return fa[x] = x == fa[x] ? x : getfa(fa[x]);
32 }
33 
34 void Tarjan(int u)
35 {
36     fa[u] = u, vis[u] = true;
37     for(int i = efst[u]; i; i = e[i].nxt)
38         if(!vis[e[i].v])
39         {
40             dis[e[i].v] = dis[u] + e[i].w;
41             Tarjan(e[i].v);
42             fa[e[i].v] = u;
43         }
44     for(int i = qfst[u]; i; i = q[i].nxt)
45     {
46         int v = q[i].u == u ? q[i].v : q[i].u;
47         if(vis[v]) lca[i >> 1] = getfa(fa[v]);
48     }
49 }
50 
51 int main()
52 {
53     int n, m, u, v, w;
54     cin >> n;
55     for(int i = 1; i < n; i++)
56     {
57         cin >> u >> v >> w;
58         addedge(i << 1, ++u, ++v, w);
59         addedge(i << 1 | 1, v, u, w);
60     }
61     cin >> m;
62     for(int i = 1; i <= m; i++)
63     {
64         cin >> u >> v;
65         addquery(i << 1, ++u, ++v);
66         addquery(i << 1 | 1, v, u);
67     }
68     Tarjan(1);
69     for(int i = 1; i <= m; i++)
70         cout << get_dis(i) << endl;
71     return 0;
72 }
View Code

  树链剖分求LCA, $O(mlogn)$,我习惯把树剖维护的7个信息写到结构体里。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 struct edge
 4 {
 5     int v, w, nxt;
 6 }e[100005];
 7 struct point
 8 {
 9     int fa, siz, son, dep, dis, dfn, top;
10 }p[50005];
11 int ptot, fst[50005];
12 
13 void addedge(int i, int u, int v, int w)
14 {
15     e[i] = (edge){v, w, fst[u]}, fst[u] = i;
16 }
17 
18 void DFS1(int u)
19 {
20     p[u].siz = 1;
21     for(int i = fst[u]; i; i = e[i].nxt)
22         if(e[i].v != p[u].fa)
23         {
24             p[e[i].v].fa = u;
25             p[e[i].v].dep = p[u].dep + 1;
26             p[e[i].v].dis = p[u].dis + e[i].w;
27             DFS1(e[i].v);
28             p[u].siz += p[e[i].v].siz;
29             if(p[e[i].v].siz > p[p[u].son].siz)
30                 p[u].son = e[i].v;
31         }
32 }
33 
34 void DFS2(int u, int top)
35 {
36     p[u].dfn = ++ptot, p[u].top = top;
37     if(p[u].son) DFS2(p[u].son, top);
38     for(int i = fst[u]; i; i = e[i].nxt)
39         if(e[i].v != p[u].fa && e[i].v != p[u].son)
40             DFS2(e[i].v, e[i].v);
41 }
42 
43 int LCA(int u, int v)
44 {
45     while(p[u].top != p[v].top)
46     {
47         if(p[p[u].top].dep > p[p[v].top].dep) swap(u, v);
48         v = p[p[v].top].fa;
49     }
50     if(p[u].dep > p[v].dep) swap(u, v);
51     return u;
52 }
53 
54 
55 int main()
56 {
57     int n, m, u, v, w;
58     cin >> n;
59     for(int i = 1; i < n; i++)
60     {
61         cin >> u >> v >> w;
62         addedge(i << 1, ++u, ++v, w);
63         addedge(i << 1 | 1, v, u, w);
64     }
65     p[1].dep = 1, DFS1(1), DFS2(1, 1);
66     cin >> m;
67     while(m--)
68     {
69         cin >> u >> v, u++, v++;
70         cout << p[u].dis + p[v].dis - 2 * p[LCA(u, v)].dis << endl;
71     }
72     return 0;
73 }
View Code

   LCT, $O(mlog^{2}n)$

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 struct LCT
  4 {
  5     int fa, c[2], rev, key, sum;
  6     int& operator [] (int i)
  7     {
  8         return c[i];
  9     }
 10 }a[100005];
 11 int sta[100005], top;
 12 
 13 void push_up(int k)
 14 {
 15     a[k].sum = a[a[k][0]].sum + a[a[k][1]].sum + a[k].key;
 16 }
 17 
 18 void push_down(int k)
 19 {
 20     if(a[k].rev)
 21     {
 22         a[a[k][0]].rev ^= 1, a[a[k][1]].rev ^= 1;
 23         swap(a[k][0], a[k][1]), a[k].rev = 0;
 24     }
 25 }
 26 
 27 bool isroot(int x)
 28 {
 29     return a[a[x].fa][0] != x && a[a[x].fa][1] != x;
 30 }
 31 
 32 void rotate(int x)
 33 {
 34     int y = a[x].fa, z = a[y].fa;
 35     int dy = a[y][1] == x, dz = a[z][1] == y;
 36     if(!isroot(y)) a[z][dz] = x;
 37     a[y][dy] = a[x][dy ^ 1], a[a[x][dy ^ 1]].fa = y;
 38     a[x][dy ^ 1] = y, a[y].fa = x, a[x].fa = z;
 39     push_up(y);
 40 }
 41 
 42 void splay(int x)
 43 {
 44     sta[top = 1] = x;
 45     for(int i = x; !isroot(i); i = a[i].fa)
 46         sta[++top] = a[i].fa;
 47     while(top)
 48         push_down(sta[top--]);
 49     while(!isroot(x))
 50     {
 51         int y = a[x].fa, z = a[y].fa;
 52         if(!isroot(y))
 53             if(a[y][1] == x ^ a[z][1] == y) rotate(x);
 54             else rotate(y);
 55         rotate(x);
 56     }
 57     push_up(x);
 58 }
 59 
 60 void access(int x)
 61 {
 62     for(int i = 0; x; x = a[x].fa)
 63         splay(x), a[x][1] = i, i = x;
 64 }
 65 
 66 void make_root(int x)
 67 {
 68     access(x), splay(x), a[x].rev ^= 1;
 69 }
 70 
 71 void link(int x, int y)
 72 {
 73     make_root(x), a[x].fa = y;
 74 }
 75 
 76 void cut(int x, int y)
 77 {
 78     make_root(x), access(y), splay(y), a[y][0] = a[x].fa = 0;
 79 }
 80 
 81 int find_root(int x)
 82 {
 83     access(x), splay(x);
 84     while(a[x][0])
 85         x = a[x][0];
 86     return x;
 87 }
 88 
 89 int main()
 90 {
 91     int n, m, u, v, w;
 92     cin >> n;
 93     for(int i = 1; i < n; i++)
 94     {
 95         cin >> u >> v >> w;
 96         a[i + n].sum = a[i + n].key = w;
 97         link(++u, i + n), link(i + n, ++v);
 98     }
 99     cin >> m;
100     while(m--)
101     {
102         cin >> u >> v;
103         make_root(++u), access(++v), splay(v);
104         printf("%d
", a[v].sum);
105     }
106     return 0;
107 }
View Code
原文地址:https://www.cnblogs.com/CtrlCV/p/5376386.html