题目:
题意:
有n座城市通过m条双向道路相连,每条道路都有一个危险系数。你的任务是回答若干个询问,每个询问包含一个起点s和一个终点t,要求找到一条从s到t的路,使得途经所有边的最大危险系数最小。
输入最多包含5组数据。(2 <= n <= 50000 , 1 <= m <= 100000 )。询问有Q个(1 <= Q <= 50000 )。起点编号1~n。
分析:
就是求任意两点的最小瓶颈路
那么肯定就是先求MST了
如果n的范围小一点,那么O(n^2)的时间复杂度可以预处理mincost[u][v],但是这题显然会超时
我的做法是树链剖分,然后线段树求区间最值
书上给的做法是用RMQ维护LCA,然后在树上用RMQ维护最值
代码:
树链剖分:
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int N = 50010;
struct EDGE {
int v,nex;
} edge[N<<1];
int head[N],tot;
int dep[N],p[N],fa[N],top[N],son[N],siz[N];
int cnt,maxn[N<<2];
void addedge(int a,int b ) {
edge[tot].v=b;
edge[tot].nex=head[a];
head[a]=tot++;
}
void dfs(int u) {
siz[u]=1,son[u]=0;
for(int i=head[u]; ~i; i=edge[i].nex) {
int v=edge[i].v;
if(v!=fa[u]) {
fa[v]=u;
dep[v]=dep[u]+1;
dfs(v);
if(siz[v]>siz[son[u]])son[u]=v;
siz[u]+=siz[v];
}
}
}
void build(int u,int tp) {
p[u]=++cnt;
top[u]=tp;
if(son[u])build(son[u],tp);
for(int i=head[u]; ~i; i=edge[i].nex) {
int v=edge[i].v;
if(v!=son[u]&&v!=fa[u])build(v,v);
}
}
void update(int p,int x,int l,int r,int rt) {
if(l==r) {
maxn[rt]=x;
return;
}
int m=l+r>>1;
if(p<=m)update(p,x,lson);
else update(p,x,rson);
maxn[rt]=max(maxn[rt<<1],maxn[rt<<1|1]);
}
int query(int a,int b,int l,int r,int rt) {
if(a<=l&&r<=b)return maxn[rt];
int m=l+r>>1;
int ret=0;
if(a<=m)ret=max(ret,query(a,b,lson));
if(b>m)ret=max(ret,query(a,b,rson));
return ret;
}
int find(int a,int b) {
int f1=top[a],f2=top[b],tmp=0;
while(f1!=f2) {
if(dep[f1]<dep[f2])swap(f1,f2),swap(a,b);
tmp=max(tmp,query(p[f1],p[a],1,cnt,1));
a=fa[f1],f1=top[a];
}
if(a==b)return tmp;
if(dep[a]>dep[b])swap(a,b);
return max(tmp,query(p[son[a]],p[b],1,cnt,1));
}
struct Edge {
int x, y, w;
bool operator < (const Edge& rhs) const {
return w < rhs.w;
}
};
const int maxm = 100000;
Edge e[maxm];
int pa[N],d[N][3];
int findset(int x) {
return pa[x] != x ? pa[x] = findset(pa[x]) : x;
}
int main() {
int kase = 0, n, m, x, y, w, Q;
//freopen("f.txt","r",stdin);
while(scanf("%d%d", &n, &m) == 2 && n) {
for(int i = 0; i < m; i++) {
scanf("%d%d%d", &x, &y, &w);
e[i] = (Edge) {
x, y, w
};
}
sort(e, e+m);
for(int i = 0; i <= n; i++) {
pa[i] = i;
}
tot=cnt=0;
memset(head,-1,sizeof(head));
for(int i = 0; i < m; i++) {
int x = e[i].x, y = e[i].y, w = e[i].w, u = findset(x), v = findset(y);
if(u != v) {
pa[u] = v;
addedge(x,y);
addedge(y,x);
++cnt;
d[cnt][0]=x;
d[cnt][1]=y;
d[cnt][2]=w;
}
}
cnt=tot=0;
fa[1]=dep[1]=0;
memset(siz,0,sizeof(siz));
memset(maxn,0,sizeof(maxn));
dfs(1);
build(1,1);
for(int i=1; i<n; i++) {
if(dep[d[i][0]]>dep[d[i][1]])swap(d[i][0],d[i][1]);
update(p[d[i][1]],d[i][2],1,cnt,1);
}
if(++kase != 1) printf("\n");
scanf("%d", &Q);
while(Q--) {
scanf("%d%d", &x, &y);
printf("%d\n",find(x,y));
}
}
return 0;
}
RMQ和LCA:
// UVa11354 Bond
// Rujia Liu
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 10;
const int logmaxn = 20;
const int INF = 1000000000;
struct LCA {
int n;
int fa[maxn]; // 父亲数组
int cost[maxn]; // 和父亲的费用
int L[maxn]; // 层次(根节点层次为0)
int anc[maxn][logmaxn]; // anc[p][i]是结点p的第2^i级父亲。anc[i][0] = fa[i]
int maxcost[maxn][logmaxn]; // maxcost[p][i]是i和anc[p][i]的路径上的最大费用
// 预处理,根据fa和cost数组求出anc和maxcost数组
void preprocess() {
for(int i = 0; i < n; i++) {
anc[i][0] = fa[i];
maxcost[i][0] = cost[i];
for(int j = 1; (1 << j) < n; j++) anc[i][j] = -1;
}
for(int j = 1; (1 << j) < n; j++)
for(int i = 0; i < n; i++)
if(anc[i][j-1] != -1) {
int a = anc[i][j-1];
anc[i][j] = anc[a][j-1];
maxcost[i][j] = max(maxcost[i][j-1], maxcost[a][j-1]);
}
}
// 求p到q的路径上的最大权
int query(int p, int q) {
int tmp, log, i;
if(L[p] < L[q]) swap(p, q);
for(log = 1; (1 << log) <= L[p]; log++);
log--;
int ans = -INF;
for(int i = log; i >= 0; i--)
if (L[p] - (1 << i) >= L[q]) {
ans = max(ans, maxcost[p][i]);
p = anc[p][i];
}
if (p == q) return ans; // LCA为p
for(int i = log; i >= 0; i--)
if(anc[p][i] != -1 && anc[p][i] != anc[q][i]) {
ans = max(ans, maxcost[p][i]);
p = anc[p][i];
ans = max(ans, maxcost[q][i]);
q = anc[q][i];
}
ans = max(ans, cost[p]);
ans = max(ans, cost[q]);
return ans; // LCA为fa[p](它也等于fa[q])
}
};
LCA solver;
#include<cstdio>
#include<vector>
int pa[maxn];
int findset(int x) {
return pa[x] != x ? pa[x] = findset(pa[x]) : x;
}
vector<int> G[maxn], C[maxn];
void dfs(int u, int fa, int level) {
solver.L[u] = level;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if(v != fa) {
solver.fa[v] = u;
solver.cost[v] = C[u][i];
dfs(v, u, level+1);
}
}
}
struct Edge {
int x, y, d;
bool operator < (const Edge& rhs) const {
return d < rhs.d;
}
};
const int maxm = 100000;
Edge e[maxm];
int main() {
int kase = 0, n, m, x, y, d, Q;
while(scanf("%d%d", &n, &m) == 2 && n) {
for(int i = 0; i < m; i++) {
scanf("%d%d%d", &x, &y, &d);
e[i] = (Edge) {
x-1, y-1, d
};
}
// 最小生成树
sort(e, e+m);
for(int i = 0; i < n; i++) {
pa[i] = i;
G[i].clear();
C[i].clear();
}
for(int i = 0; i < m; i++) {
int x = e[i].x, y = e[i].y, d = e[i].d, u = findset(x), v = findset(y);
if(u != v) {
pa[u] = v;
G[x].push_back(y);
C[x].push_back(d);
G[y].push_back(x);
C[y].push_back(d);
}
}
// 化成有根树
solver.n = n;
dfs(0, -1, 0);
solver.preprocess();
if(++kase != 1) printf("\n");
scanf("%d", &Q);
while(Q--) {
scanf("%d%d", &x, &y);
printf("%d\n", solver.query(x-1, y-1));
}
}
return 0;
}

本文介绍了一种求解任意两点间最小瓶颈路的方法。利用树链剖分和RMQ技术来高效解决该问题,并提供了两种不同的实现思路及其代码示例。
2022

被折叠的 条评论
为什么被折叠?



