【13NOIP提高组】货车运输(洛谷P1967)(Acwing.506)(一本通1877)
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。
每一条道路对车辆都有重量限制,简称限重。
现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入格式
输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路,注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市 运输货物到 y 城市,注意:x 不等于 y。
输出格式
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。
如果货车不能到达目的地,输出-1。
【输入样例】
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
【输出样例】
3
-1
3
【提示】
对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q < 1,000;
对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q < 1,000;
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。
LCA的经典应用:静态树上链的权值查询问题
首先我们考虑找这个无向图的最大生成森林(用Krusal即可),因为根据题意,要求两点之间路径中最小权值的最大值。那么在两点之间,我们必然选择权值尽可能大的边构成这两点间的路径,使得路径中的最小权值尽可能大。
然后考虑对于每辆货车,如果它的起点和终点不在同一棵树上,说明不能到达目的地,输出-1。否则,我们就可以运用倍增LCA,在求出f数组的同时求dp数组。其中dp[i][j]指从点i出发向上走2^j步 经过的所有路径上的边权最小值。
(关于倍增LCA:可以看看这个)
状态转移方程:dp[i][0]=w(i,f[i][0])
dp[i][j]=min(dp[i][j-1],dp[f[i][j-1]][j-1])
时间复杂度O( mlogm+ nlogn+ qlogn)
1 #include<bits/stdc++.h>
2 #define int long long
3 #define R register int
4 #define PII pair<int,int>
5 using namespace std;
6 const int N=1e6+5,inf=0x3f3f3f3f;
7 inline int read()
8 {
9 char ch=getchar();int num=0;bool flag=false;
10 while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
11 while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();}
12 return flag?-num:num;
13 }
14 int n,cnt,m,head[N],deep[N],f[N][20],s,fa[N],dp[N][20];
15 struct Node{
16 int x,y,z;
17 bool operator <(const Node &b)const
18 {
19 return z>b.z;
20 }
21 }ed[N];//存无向图中的边
22 struct node{
23 int to,nxt,w;
24 }e[N];//存最大生成森林中的边
25 int find(int x){return x==fa[x]?fa[x]:fa[x]=find(fa[x]);}
26 void add(int x,int y,int z)
27 {
28 e[++cnt].nxt=head[x];
29 head[x]=cnt;
30 e[cnt].to=y;
31 e[cnt].w=z;
32 }
33 void dfs(int x,int fa)
34 {
35 deep[x]=deep[fa]+1;
36 f[x][0]=fa;
37 for(R i=head[x];i;i=e[i].nxt)
38 {
39 int t=e[i].to;
40 if(t!=fa)
41 {
42 dp[t][0]=e[i].w;//dp[i][0]=w(i,f[i][0])
43 dfs(t,x);
44 }
45 }
46 }
47 int lca(int x,int y)//直接返回路径中的边权最小值
48 {
49 if(deep[x]<deep[y])swap(x,y);
50 if(x==y)return 0;
51 int ans=inf;
52 for(R j=17;j>=0;j--)
53 if(deep[f[x][j]]>=deep[y])
54 {
55 ans=min(ans,dp[x][j]);
56 x=f[x][j];
57 }
58
59 if(x==y)return ans;
60 for(R j=17;j>=0;j--)
61 if(f[x][j]!=f[y][j])
62 {
63 ans=min(ans,dp[x][j]);
64 ans=min(ans,dp[y][j]);
65 x=f[x][j];
66 y=f[y][j];
67 }
68 ans=min(ans,dp[x][0]);ans=min(ans,dp[y][0]);
69 return ans;
70 }
71 signed main()
72 {
73 n=read();m=read();
74 for(R i=1;i<=m;i++)
75 {
76 ed[i].x=read(),ed[i].y=read(),ed[i].z=read();
77 }
78 sort(ed+1,ed+1+m);
79 for(R i=1;i<=n;i++)fa[i]=i;
80 for(R i=1;i<=m;i++)//求最大生成树
81 {
82 int x=find(ed[i].x),y=find(ed[i].y);
83 if(x!=y)
84 {
85 fa[x]=y;
86 add(ed[i].x,ed[i].y,ed[i].z);
87 add(ed[i].y,ed[i].x,ed[i].z);
88 }
89 }
90 for(R i=1;i<=n;i++)
91 {
92 if(fa[i]==i)//有多个最大生成树
93 {
94 dfs(i,0);
95 dp[i][0]=inf;
96 }
97 }
98 for(R j=1;j<=17;j++)
99 for(R i=1;i<=n;i++)
100 {
101 f[i][j]=f[f[i][j-1]][j-1];
102 dp[i][j]=min(dp[f[i][j-1]][j-1],dp[i][j-1]);
103 }
104
105 int q=read();
106 while(q--)
107 {
108 int x=read(),y=read();
109 if(find(x)!=find(y))puts("-1");//起点和终点不连通
110 else
111 {
112 printf("%lld\n",lca(x,y));
113 }
114 }
115 return 0;
116 }