【货车运输】

作者认为这是一道非常好的题目,既包含了生成树,又包含了求lca以及倍增的思想


进入正题:

容易想到为了让火车最大载重尽量大,我们需要把原图变为它的最大生成树。接下来每次询问两点我们只需要求其简单路径上的边权最小值即可。

还记得ST表吗?我们也是用倍增的思想存储最小值,这里我们同样用倍增。建立一个结构体:

1
2
3
4
struct tree {
int fa,w;
tree():w(99999999){};//附初值为正无穷
} anc[100005][20];

fa用来表示其祖先节点,w表示它到它祖先节点路径上边权最小值,因此我们得到状态转移方式:

1
2
3
4
5
6
anc[to][0].fa=x;//x为to的根节点
anc[to][0].w=path[x][i].w;//到其根节点的权值
for(int i=1; i<=19; i++) {
anc[to][i].fa=anc[anc[to][i-1].fa][i-1].fa;
anc[to][i].w=min(anc[to][i-1].w,anc[anc[to][i-1].fa][i-1].w);
}

此处有些难以理解(并不),认真想想就能明白。
最后是求整条路径上的最小值,和求lca的代码类似,但多了一些操作:

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
void lca(int l,int r) {
//ans保存路径上最小值,初始化为正无穷
if(d[l]<d[r]) {
swap(l,r);
}
for(int i=19; i>=0; i--) {
if(d[anc[l][i].fa]>=d[r]) {
ans=min(ans,anc[l][i].w);
//一旦能往上跳就更新ans
l=anc[l][i].fa;
}
}
if(l==r)return ;
for(int i=19; i>=0; i--) {
if(anc[l][i].fa!=anc[r][i].fa) {
ans=min(ans,anc[l][i].w);
ans=min(ans,anc[r][i].w);
l=anc[l][i].fa;
r=anc[r][i].fa;
}
}
//记得下面两句话,因为我们最终左右端点还没跳到lca上
ans=min(ans,anc[l][0].w);
ans=min(ans,anc[r][0].w);
return ;
}

附上完整代码:

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
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,m,f[maxn],d[maxn],ans,q;
struct edge {
int u,v,w;
edge(int a=0,int b=0) {
v=a,w=b;
}
} ed[maxn];
struct tree {
int fa,w;
tree():w(99999999){};
} anc[maxn][20];
vector<edge> path[maxn];
bool cmp(edge x,edge y) {
return x.w>y.w;
}
int find(int x) {
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
void dfs(int x) {
for(int i=0; i<(int)path[x].size(); i++) {
int to=path[x][i].v;
if(d[to])continue;
d[to]=d[x]+1;
anc[to][0].fa=x;
anc[to][0].w=path[x][i].w;
for(int i=1; i<=19; i++) {
anc[to][i].fa=anc[anc[to][i-1].fa][i-1].fa;
anc[to][i].w=min(anc[to][i-1].w,anc[anc[to][i-1].fa][i-1].w);
}
dfs(to);
}
}
void lca(int l,int r) {
if(d[l]<d[r]) {
swap(l,r);
}
for(int i=19; i>=0; i--) {
if(d[anc[l][i].fa]>=d[r]) {
ans=min(ans,anc[l][i].w);
l=anc[l][i].fa;
}
}
if(l==r)return ;
for(int i=19; i>=0; i--) {
if(anc[l][i].fa!=anc[r][i].fa) {
ans=min(ans,anc[l][i].w);
ans=min(ans,anc[r][i].w);
l=anc[l][i].fa;
r=anc[r][i].fa;
}
}
ans=min(ans,anc[l][0].w);
ans=min(ans,anc[r][0].w);
return ;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++) {
scanf("%d%d%d",&ed[i].u,&ed[i].v,&ed[i].w);
}
sort(ed+1,ed+1+m,cmp);
for(int i=1; i<=n; i++)f[i]=i;
int cnt=0;
for(int i=1; i<=m; i++) {
int t1=find(ed[i].u),t2=find(ed[i].v);
if(t1==t2)continue;
f[t1]=t2;
path[ed[i].u].push_back(edge(ed[i].v,ed[i].w));
path[ed[i].v].push_back(edge(ed[i].u,ed[i].w));
cnt++;
if(cnt==n-1)break;
}
for(int i=1;i<=n;i++){
if(d[i])continue;
d[i]=1;
dfs(i);
}
scanf("%d",&q);
for(int i=1; i<=q; i++) {
int a,b;
scanf("%d%d",&a,&b);
if(find(a)!=find(b)){
printf("-1\n");
continue;
}
ans=99999999;
lca(a,b);
printf("%d\n",ans);
}
}

完结撒花~~