ACM_JLINE.

LCA(寒假课件整理)

字数统计: 1.6k阅读时长: 9 min
2019/02/27 Share

How far away

原题链接:HDU2586

题目大意 : 求一棵树上任意两点的最短距离(裸题)

题解 : 最近公共最先裸题 path(l, r) = dist[l] + dist[r] - 2 * dist[lca(l, r)

Code block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

const int Maxn = 40005;
int n, m, l, r, x, t, T;
int dist[Maxn], d[Maxn], f[Maxn][35];
struct node{
int to, w;
};
vector <node> V[Maxn];
queue <int> q;
void BFS(){
q.push(1); d[1] = 1;
while(q.size())
{
int x = q.front(); q.pop();
for(int i = 0; i < V[x].size(); i++)
{
int y = V[x][i].to;
int w = V[x][i].w;
if(d[y]) continue;
d[y] = d[x] + 1;
dist[y] = dist[x] + w;
f[y][0] = x;
for(int j = 1; j <= t; j++)
{
f[y][j] = f[f[y][j-1]][j-1];
}
q.push(y);
}
}
}

int lca(int x, int y)
{
if(d[x] > d[y]) swap(x, y);
for(int i = t; i >= 0; i--)

if(d[f[y][i]] >= d[x]) y = f[y][i];

if(x == y) return x;
for(int i = t; i >= 0; i--)
{
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
}
return f[x][0];
}
int main(){
cin >> T;
while(T--)
{
cin >> n >> m;
memset(dist, 0, sizeof(dist));
memset(d, 0, sizeof(d));
memset(f, 0, sizeof(f));
t = (int)(log(n) / log(2)) + 1;
for(int i=1;i<=n;i++) V[i].clear();
for(int i = 1; i < n; i++)
{
cin >> l >> r >> x;
V[l].push_back((node){r,x});
V[r].push_back((node){l,x});
}
BFS();
for(int i = 1; i <= m; i++)
{
cin >> l >> r;
cout << dist[l] + dist[r] - 2 * dist[lca(l, r)] << endl;
}
}
return 0;
}

Network

原题链接:Poj3417

题目大意 : 在原先的树上再添加几条边, 问原先树上的边(称为主边)被后面添加的边覆盖的次数(称为附加边)

题解 : LCA + 树上差分。 每添加一条附加边, 就是在这个区间内(两点之间的)所有主边被覆盖多了一次, 那么可以用差分的思想, 只要把两个端点加1, 他们的最近公共祖先减2
。sta[x] 表示以 x 为根节点的子树中各节点的权值(dif[i])和。 那么 sta[x] 就是 x 与它父亲之间的边覆盖的次数。

Code block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <cmath>
using namespace std;

const int Maxn = 1e6+5;
int n, m, x, y, cnt, ans;
int d[Maxn], dist[Maxn], ver[Maxn], head[Maxn], Next[Maxn];
int f[Maxn][30], t, vis[Maxn], sta[Maxn], dif[Maxn];
void add(int x, int y)
{
ver[++cnt] = y;
Next[cnt] = head[x];
head[x] = cnt;
}

queue <int> q;
void bfs()
{
q.push(1); d[1] = 1;
while(q.size())
{
int x = q.front(); q.pop();
for(int i = head[x]; i; i = Next[i])
{
int y = ver[i];
if(d[y]) continue;
d[y] = d[x] + 1;
f[y][0] = x;
for(int j = 1; j <= t; j++)
{
f[y][j] = f[f[y][j-1]][j-1];
}
q.push(y);
}
}
}
int lca(int x, int y)
{
if(d[x] > d[y]) swap(x, y);
for(int i = t; i >= 0; i--)
{
if(d[f[y][i]] >= d[x]) y = f[y][i];
}
if(x == y) return x;
for(int i = t; i >= 0; i--)
{
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
}
return f[x][0];
}

int dfs(int x)
{
vis[x] = 1;
sta[x] = dif[x];
for(int i = head[x]; i; i = Next[i])
{
int y = ver[i];
if(vis[y]) continue;
sta[x] += dfs(y);
}
return sta[x];
}
int main()
{
scanf("%d%d", &n, &m);
t = (int)(log(n)/log(2)) + 1;
for(int i = 1; i < n; i++)
{
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
bfs();
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &x, &y);
dif[x]++;
dif[y]++;
dif[lca(x, y)] -= 2;
}
dfs(1);

for(int i = 1; i <= n; i++)
{
if(sta[i] == 0 && i != 1){
ans += m;
}
if(sta[i] == 1) ans ++;
}
printf("%d\n", ans);
return 0;
}

异象石

原题链接:CH#56C

题目大意 : 在树上有以下 3 种操作:

  1. 某个点上出现异象石
  2. 某个点上的异象石消失
  3. 求所有异象石所在点连通的边集的总长度最小

题解 :用 set 来维护答案, 按时间戳维护异象石出现消失, path(x, y) 代为x, y的路径长度。 如果出现新的异象石, 按时间戳插入, 记该点为 x ,前后分别为 l 和 r; 那么总答案减去 path(l, r), 加上 path(l, x) + path(x, r)。 若消失了一个异象石, 反之即可。

Code block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int Maxn = 2e5+5;

int Next[Maxn], head[Maxn], ver[Maxn], edge[Maxn], dfn[Maxn], f[Maxn][30], d[Maxn], a[Maxn];
int x, y, z, n, m, tot, t;
ll dis[Maxn], ans;
char ch[5];

void bfs()
{
queue <int> q;
q.push(1); d[1] = 1;
while(q.size())
{
int x = q.front(); q.pop();
for(int i = head[x]; i; i = Next[i])
{
int y = ver[i]; if(d[y]) continue;
d[y] = d[x] + 1; q.push(y);
f[y][0] = x; dis[y] = dis[x] + edge[i];
for(int j = 1; j <= t; j++){
f[y][j] = f[f[y][j-1]][j-1];
}
}
}
}

int cnt;
void dfs(int x)
{
dfn[x] = ++cnt; a[cnt] = x;
for(int i = head[x]; i; i = Next[i])
{
int y = ver[i];
if(dfn[y]) continue;
dfs(y);
}
}
void add(int x, int y, int z)
{
ver[++tot] = y; Next[tot] = head[x];
head[x] = tot; edge[tot] = z;
}
int lca(int x, int y)
{
if(d[x] > d[y]) swap(x, y);
for(int i = t; i >= 0; i--){
if(d[f[y][i]] >= d[x]) y = f[y][i];
}
if(x == y) return x;
for(int i = t; i >= 0; i--)
{
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
}
return f[x][0];
}

ll path(int x, int y)
{
return dis[x] + dis[y] - 2 * dis[lca(x, y)];
}

set <int> s;
typedef set <int> :: iterator It;
It it;

It L(It it)
{
if(it == s.begin()) return --s.end();
return --it;
}

It R(It it)
{
if(it == --s.end()) return s.begin();
return ++it;
}

int main()
{
scanf("%d", &n);
t = (int)(log(n) / log(2)) + 1;
for(int i = 1; i < n; i++)
{
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
dfs(1);
bfs();
scanf("%d", &m);
for(int i = 1; i <= m; i++)
{
scanf("%s", ch);
if(ch[0] == '+')
{
scanf("%d", &x);
if(s.size())
{
it = s.upper_bound(dfn[x]);
if(it == s.end()) it = s.begin();
int l = *L(it), r = *it;
ans += path(a[l], x) + path(x, a[r]) - path(a[l], a[r]);
}
s.insert(dfn[x]);
}
else if(ch[0] == '-')
{
scanf("%d", &x);
it = s.find(dfn[x]);
int l = *L(it), r = *R(it);
ans -= path(x, a[l]) + path(x, a[r]) - path(a[l], a[r]);
//ans = ans - path(a[l], x) - path(x, a[r]) + path(a[l], a[r]);
s.erase(dfn[x]);
}
else
{
printf("%lld\n", ans/2);
}
}
return 0;
}

记得原本题目挺多了, 现在翻修好多不见了, emmmmm

CATALOG
  1. 1. How far away
    1. 1.0.1. Code block
  • 2. Network
    1. 2.0.1. Code block
  • 3. 异象石
    1. 3.0.1. Code block