S国有 N 座城市,编号依次从 1 到 N,城市之间有 M 条双向的道路。每一条道路对每辆车都有一个
最大载重量。小明意外发现了一批宝藏,精心策划了下,他计划用 Q 辆货车分头秘密的运送这批宝藏。
他想知道每辆车在不超过道路最大载重量的情况下,最多能运送宝藏的重量(此处忽略货车的重量,
只考虑宝藏的重量)。 输入格式
输入第一行输入两个正整数N,M(0<N<10,000,0<M<50,000),之间用一个空格隔开,表示S国的 N 座城市和 M 条道路。 接下来输入 M 行,每行输入三个正整数 x、y、z,每两个整数之间用一个空格隔开,表示编号为 x 的城市和编号为 y 的城市之间有一条最大载重量为 z的道路。x不等于 y,两座城市之间可能有多条道
路。 接下来输入一行,输入一个正整数Q(0<Q<30,000),表示有 Q 辆货车在运送宝藏。 接下来输入 Q 行,每行输入两个整数 a、b,之间用一个空格隔开,表示一辆货车需要从 a 城市运输
宝藏到 b 城市,注意:a 不等于 b。 输出格式:输出一共有 Q行,每行输出一个整数,表示对于每一辆货车,在不超过道路最大载重量的
情况下,最多能运送宝藏的重量。如果货车不能到达目的地,输出 −1。
样例输入
5 4
1 2 4
2 3 3
3 4 5
3 1 1
2
1 3
2 4
样例输出
3
3
首先这道题得预处理一下,因为每两条路之间有不止一条路
定理1:最大生成树中任意两点之间的权边的最小值是所有生成树中该两点的权边最小值集合中的最大值
证明:
由于从u到v点一定有一条通路相连,如果另外有一条边加入改生成树后会形成另外一条从u到v的通路(包括形成一个包含u-v边的环的情况),如果该边的权值比通路中的最小的权边要大,则可以取代最小的边形成更大的生成树,矛盾。所以不存在这种边了。
所以,这道题我们得用最大生成树优化一下。
但是形成最大生成树之后我们又该如何搜索两个点通路间的最小边的权值是多少呢,如果用遍历搜索就是O(V)的复杂度。但是如果用倍增算法的话时间复杂度就会降到O(logV)
1:这里讲一下如何用倍增算法来记录最小权边
我们用baozang[x][i]的值来记录从x点到它的2^i倍祖先的最小权值。
dfs深度搜索的时候可以用baozang[x][0]记录x点到父亲的边权值【最大生成树中每个点只有一个父亲,根节点除外】
然后再用baozang[x][i]=min(baozang[x][father[x][i-1]],baozang[father[x][i-1]][i])递推式去更新每个点的baozang数组;
2,讲一下怎么利用baozang数组
我们在lca函数里面可以利用倍增算法,寻找到任意两点x,y的最近公共祖先。所以会形成两个通路(从x,y分别到最近公共祖先的通路)在寻找的时候同时利用min_x,和min_y来记录这个通路上的最小边。注意利用倍增二分跳跃的最后x,y两点的父节点才是公共祖先。所以还需要最后更新一下min_x和min_y的值。然后返回min_x和min_y的最小值。
直接上代码
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAX_N=10000;
const int MAX_M=50000;
const int inf=0x3f3f3f3f;
int head[MAX_N];
int depth[MAX_N];
int fa[MAX_N][21];
int father[MAX_N];
int baozang[MAX_N][21];
bool visit[MAX_N];
int n,m,q;
int ans=0;
struct edge{
int w;
int to;
int next;
}eid[MAX_N];
struct EDGE{
int u;int v;int w;
}e[MAX_M];
bool cmp(EDGE& a,EDGE& b){
return a.w>b.w;
}
int ancestor(int a){
if(a==father[a])
return a;
else return father[a]=ancestor(father[a]);
}
int same(int a,int b){
return ancestor(a)==ancestor(b);
}
void merge(int x,int y){
int fa_x=ancestor(x);
int fa_y=ancestor(y);
father[fa_x]=fa_y;
}
void insert(int u,int v,int w){
eid[ans].w=w;
eid[ans].to=v;
eid[ans].next=head[u];
head[u]=ans++;
}
void swap(int &a,int &b){
int temp=a;
a=b;
b=temp;
}
void dfs(int now,int dep,int fat,int bz){
if(visit[now])
return;
visit[now]=true;
depth[now]=dep;
fa[now][0]=fat;
baozang[now][0]=bz;
for(int i=head[now];i!=-1;i=eid[i].next){
dfs(eid[i].to,dep+1,now,eid[i].w);
}
}
int read(){
int w=1;
int s=0;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')
w*=-1;
ch=getchar();
}
while(isdigit(ch)){
s=(s<<1)+(s<<3)+ch-48;
ch=getchar();
}
return w*s;
}
int lca(int x,int y){
int min_x=inf;
int min_y=inf;
if(depth[x]<depth[y]){
swap(x,y);
}
for(int i=20;i>=0;--i){
if(fa[x][i]!=0 && depth[fa[x][i]]>=depth[y]) {
min_x=min(min_x,baozang[x][i]);
x = fa[x][i];
}
}
if(x==y)
return min_x;
else{
for(int i=20;i>=0;--i){
if(fa[x][i]!=0 && fa[y][i]!=0 && fa[x][i]!=fa[y][i])
{
min_x=min(min_x,baozang[x][i]);
min_y=min(min_y,baozang[y][i]);
x=fa[x][i];
y=fa[y][i];
}
}
}
min_x=min(min_x,baozang[x][0]);
min_y=min(min_y,baozang[y][0]);
return min(min_x,min_y);
}
void kruskal(){
for(int i=1;i<=n;i++)
father[i]=i;
sort(e+1,e+m+1,cmp);
int left=n;
for(int i=1;i<=m && left>1;++i){
int u=e[i].u;int v=e[i].v;
if(same(u,v)==1)
continue;
else{
insert(u,v,e[i].w);
insert(v,u,e[i].w);
left--;
merge(u,v);
}
}
}
int main() {
cin>>n>>m;
memset(head,-1, sizeof(head));
memset(visit, false, sizeof(visit));
for(int i=1;i<=m;i++){
e[i].u=read();
e[i].v=read();
e[i].w=read();
}
kruskal();
dfs(1,1,0,0);
for(int i=1;i<=20;++i){
for(int j=1;i<=n;++j){
fa[j][i]=fa[fa[j][i-1]][i-1];
baozang[j][i]=min(baozang[j][i-1],baozang[fa[j][i-1]][i-1]);
}
}
cin>>q;
for(int i=0;i<q;++i){
int a,b;
a=read();
b=read();
cout<<lca(a,b)<<endl;
}
return 0;
}