洛谷T1967 货车运输 Kruskal最大生成树&&倍增LCA

这题的题意是:对于每组x、y,求x到y路径上最小边权的最大值。
于是可以使用最大生成树,因为最大生成树满足性质:生成树中最小边权最大,且任意两点间路径上最小边权最大。
有了树之后,要求路径,那就要考虑LCA。
首先,这题可以树剖,但是我太懒了,于是写了倍增233
具体搞法:
Kruskal跑出最大生成树,然后在树上倍增LCA,处理出2个数组:fa[][]和minv[][];fa[][]表示第2^k个父亲是谁,minv[][]表示当前节点到其第2^k个父亲的路径上的最小边权。对于每次查询,在求LCA的同时,沿路将各段的minv取并即可,每次时间与LCA查询相同,为logn。
现在还有一个问题:图不一定是连通的。。。不能无脑LCA。。。
解决这个问题的方法是:在Kruskal结束后,对并查集中的每个集合各进行一次LCA预处理。对于每次查询,先判断两节点是否属于同一集合,若属于同一集合则进行LCA查询,否则直接输出-1。
代码如下:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<ctime>
  6 #include<cstdlib>
  7
  8 #include<string>
  9 #include<stack>
 10 #include<queue>
 11 #include<vector>
 12 #include<algorithm>
 13 #include<map>
 14 #include<set>
 15
 16 #define inf 2147483647
 17 #define ri register int
 18 #define ll long long
 19
 20 using namespace std;
 21
 22 inline void read(int &x){
 23     x=0;
 24     char t=getchar();
 25     bool f=0;
 26
 27     while(t<‘0‘ || t>‘9‘){
 28         if(t==‘-‘)f=1;
 29         t=getchar();
 30     }
 31
 32     while(t>=‘0‘ && t<=‘9‘){
 33         x=(x<<3)+(x<<1)+t-‘0‘;
 34         t=getchar();
 35     }
 36
 37     if(f)x=-x;
 38 }
 39
 40 inline void addedge(int,int,int);
 41 int u[100010];
 42 int v[100010];
 43 int w[100010];
 44 int first[10005];
 45 int next[100010];
 46 int pe=0;  //邻接表(无向)
 47
 48 inline bool cmp(const int a,const int b);
 49 int ha_e[50005];  //边排序辅助数组
 50
 51 inline void Kruskal();
 52 int find(int);
 53 int bcj[10005];  //并查集
 54 bool use[50005];  //记录边是否使用
 55
 56 void dfs(int,int);  //LCA预处理,直接父亲及最小边权在上层处理
 57 bool vis[10005];  //防止重复访问
 58 int dep[10005];  //节点深度
 59 int fa[10005][15];  //LCA表
 60 int minv[10005][15];  //节点到其第2^k个父亲路径间的最小边权
 61
 62 inline int query(int,int);  //查询路径最小边权
 63
 64 int n,m,q;
 65 int rest;
 66 int x,y,z;
 67
 68 int main(){
 69     read(n);read(m);
 70     rest=n;
 71
 72     for(ri i=1;i<=m;i++){
 73         read(x);read(y);read(z);
 74
 75         addedge(x,y,z);
 76         addedge(y,x,z);
 77
 78         ha_e[i]=i<<1;
 79     }  //建初始图
 80
 81     sort(ha_e+1,ha_e+1+m,cmp);  //边权从大到小排序
 82     Kruskal();  //生成最大生成树,记录需要使用的边
 83
 84     memset(vis,0,sizeof(vis));
 85
 86     for(ri i=1;i<=n;i++)  //生成2个倍增表
 87         if(!vis[find(i)]){
 88             fa[i][0]=0;  //根节点无父亲
 89             minv[i][0]=inf;
 90             dfs(i,1);  //以i号节点为根进行遍历
 91         }
 92
 93     read(q);
 94
 95     for(ri i=1;i<=q;i++){
 96         read(x);read(y);
 97
 98         if(find(x)!=find(y)){
 99             printf("-1\n");
100             continue;
101         }
102
103         if(dep[x]>dep[y])swap(x,y);  //使y更深
104
105         printf("%d\n",query(x,y));
106     }
107
108     return 0;
109 }
110
111 inline void addedge(int x,int y,int z){
112     pe++;
113     u[pe]=x;
114     v[pe]=y;
115     w[pe]=z;
116     next[pe]=first[x];
117     first[x]=pe;
118 }
119
120 inline bool cmp(const int a,const int b){
121     return w[a]>w[b];
122 }
123
124 int find(int x){
125     if(bcj[x]==x)return x;
126     else return bcj[x]=find(bcj[x]);
127 }
128
129 inline void Kruskal(){
130     int x,y,fx,fy;
131
132     for(ri i=1;i<=n;i++)bcj[i]=i;
133     memset(use,0,sizeof(use));
134
135     for(ri i=1;i<=m && rest>1;i++){
136         x=u[ha_e[i]];
137         y=v[ha_e[i]];
138         fx=find(x);
139         fy=find(y);
140
141         if(fx!=fy){
142             bcj[fx]=fy;
143             rest--;
144             use[ha_e[i]>>1]=1;
145         }
146     }
147 }
148
149 void dfs(int s,int h){
150     int e,t;
151
152     dep[s]=h;
153     vis[s]=1;
154
155     for(ri i=1;(1<<i)<h;i++){  //i=0已在上层处理;若1<<i==h则恰有一个节点越界
156         fa[s][i]=fa[fa[s][i-1]][i-1];
157         minv[s][i]=min(minv[s][i-1],minv[fa[s][i-1]][i-1]);
158     }
159
160     e=first[s];
161     t=v[e];
162
163     while(e){
164         if(!vis[t] && use[(e+1)>>1]){
165             fa[t][0]=s;
166             minv[t][0]=w[e];
167             dfs(t,h+1);
168         }
169
170         e=next[e];
171         t=v[e];
172     }
173 }
174
175 inline int query(int x,int y){
176     int dt=dep[y]-dep[x];
177     int ans=inf;
178
179     for(ri i=0;(1<<i)<=dt;i++)
180         if((1<<i)&dt){
181             ans=min(ans,minv[y][i]);
182             y=fa[y][i];
183         }
184
185     if(x==y)return ans;
186
187     for(ri i=13;i>=0;i--)
188         if(fa[x][i]!=fa[y][i]){
189             ans=min(ans,minv[x][i]);
190             ans=min(ans,minv[y][i]);
191
192             x=fa[x][i];
193             y=fa[y][i];
194         }
195
196     ans=min(ans,minv[x][0]);
197     ans=min(ans,minv[y][0]);
198
199     return ans;
200 }
时间: 2024-10-13 06:39:16

洛谷T1967 货车运输 Kruskal最大生成树&&倍增LCA的相关文章

洛谷 P1967 货车运输 Label: 倍增LCA &amp;&amp; 最小瓶颈路

题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z

洛谷P3128 [USACO15DEC]最大流Max Flow [倍增LCA]

题目描述 Farmer John has installed a new system of  pipes to transport milk between the  stalls in his barn (), conveniently numbered . Each pipe connects a pair of stalls, and all stalls are connected to each-other via paths of pipes. FJ is pumping milk

洛谷P1967 货车运输

题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z

洛谷 P1967 货车运输 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1967 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示

洛谷P1967货车运输

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#define N 100010#define INF 0xfffffusing namespace std;int n, m, q, x, y, cnt;int head[N], nxt[N*2], to[N*2], w[N*2], p[N*5], f[N][20], minv[N][20], dep[N];s

CODEVS3287货车运输 noip2013day1T3(最大生成树+倍增)

题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入描述 Input Description 第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路.接下来 m 行每行 3 个整数 x.y.z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路

$Noip2013/Luogu1967$ 货车运输 最大生成树+倍增$lca$

$Luogu$ $Sol$ 首先当然是构建一棵最大生成树,然后对于一辆货车的起点和终点倍增跑$lca$更新答案就好.记得预处理倍增的时候不仅要处理走了$2^i$步后是那个点,还有这中间经过的路径权值的最小值以便之后统计答案. 再一看发现这题并没说给的图是联通的,也就是说跑了最大生成树之后可能有若干棵树.所以构树的时候要注意不能随便选一个点构完就不管了,要对每一个联通块都构一次.其他的地方似乎没有因为它有多棵树而有什么不同,只是询问的时候看下是不是一个联通块里的就好. $Code$ #includ

货车运输(最大生成树+LCA)

题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z

luogu 1967 货车运输(最大瓶颈生成树+LCA)

题意:给出一颗n个点的图,q个询问,每次询问u到v的路径中最小的边最大是多少. 图的最大瓶颈生成树有一个性质,对于该图的任意两个点,在树中他们之间路径的最小边最大. 由于这个图不一定联通,于是我们对它的联通块都求一次最大瓶颈生成树. 每次询问就变成了在最大瓶颈生成树上找出u到v路径的最小边. 这个显然可以用LCA维护点到它的2^x祖先之间的边的最小值来解决. # include <cstdio> # include <cstring> # include <cstdlib&g