题目描述
给你N个点的无向图 (1 <= N <= 15,000),记为:1…N。
图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 < = d_j < = 1,000,000,000).现在有 K个询问 (1 < = K < = 20,000)。
每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?输入
第一行: N, M, K。
第2..M+1行: 三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N). 表示X与Y之间有一条长度为D的边。 第M+2..M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?输出
对每个询问,输出最长的边最小值是多少。
样例输入
6 6 8 1 2 5 2 3 4 3 4 3 1 4 8 2 5 7 4 6 2 1 2 1 3 1 4 2 3 2 4 5 1 6 2 6 1
样例输出
5 5 5 4 4 7 4 5
提示
1 <= N <= 15,000
1 <= M <= 30,000 1 <= d_j <= 1,000,000,000 1 <= K <= 15,000
首先根据最小生成树的性质可以知道两点间路径上最长边最小的那条路径,就是最小生成树上两点间的路径——因为最小生成树是按边权从小到大加边,所以保证两点间路径上的边都尽可能的小。那么只要先把最小生成树求出来,其他的边就没有用了。对于最小生成树求解,一种做法是倍增维护g[i][j]表示以i节点向上跳2j步路径上的边权最大值。今天来介绍另一种做法——kruskal重构树。什么是kruskal重构树呢?就是把最小生成树上的边从小到大,对于每一条边a-b边权为c,就把重构树上a所在子树的根节点和b所在子树的根节点(可以是自己)分别连到一个新建节点上,将那个点作为这两个点的父节点,而新建那个点的点权就是c。最后形成的一棵二叉树就是kruskal重构树。kruskal重构树是一个大根堆且两叶子节点的lca点权就是这两点在最小生成树上路径上的最大边权。这样对于每次询问只要找到两点lca就行了。
最小生成树+倍增
#include#include #include #include #include using namespace std;int n,m;int next[100050];int to[100050];int val[100050];int head[15010];int f[15010][20];int g[15010][20];int d[15010];int F[15010];int tot=0;int sum;int num;struct node{ int x; int y; int v;}a[50001];void add(int x,int y,int v){ tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; val[tot]=v;}int cmp(node a,node b){ return a.v =0;j--) { if(f[x][j]!=f[y][j]) { total=max(total,max(g[x][j],g[y][j])); x=f[x][j]; y=f[y][j]; } } return total=max(max(g[x][0],g[y][0]),total);}int main(){ scanf("%d%d",&n,&m);int q;scanf("%d",&q); for(int i=1;i<=n;i++) { F[i]=i; } for(int i=1;i<=m;i++) { scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v); } sort(a+1,a+1+m,cmp); num=0; sum=0; for(int j=1;j<=m;j++) { int fx=find(a[j].x); int fy=find(a[j].y); if(fx!=fy) { F[fx]=fy; num++; sum+=a[j].v; add(a[j].x,a[j].y,a[j].v); add(a[j].y,a[j].x,a[j].v); } if(num==n-1) { break; } } g[1][0]=0; dfs(1,1); int A,B; for(int i=1;i<=q;i++) { scanf("%d%d",&A,&B); printf("%d\n",lca(A,B)); }}
kruskal重构树
#include