描述
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。
测试样例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。
by 460289052
题解来自未知大牛:
最根本算法就是最大生成树+倍增 倍增很好理解 就是我想要让一个树上的点到达他祖先中的某点 而且每次拜访到他父亲是都会有一个对应的权值 那么我就预先处理两个数组: 数组f[i][j] 存储结点i的第2^j的前辈对应的数字 数组g[i][j] 存储结点i到第2^j的前辈对应的最小权值 可以经过繁杂的推导 证明 f[i][j]=f[f[i][j-1]][j-1] 即Pi的2^j-1再2^j-1就可以到达其的 2^j 再根据这个 推导出 g[i][j]=min(g[i][j-1],g[f[i][j-1]][j-1]) 即将从Pi到2次j次方祖先分为两段 第一段是从Pi到2^j-1祖先里最小的权值 第二段是从Pi的2^j-1祖先到Pi的2^j祖先中间最小的权值 从中取最小值就是对应的从Pi到它的2^j祖先中间最小的权值 这个g函数的思路就是动态规划 现在处理好了f,g两个数组 而用2作为次幂的原因是可以用2的次幂相加表示出所有的正整数 所以可以相应地缩短处理 两个结点回推到最近共同祖先的这一个过程 最大生成树在此不论 (其中有用到并查集判断是否两个结点是否处于同一棵树下,用kruscal算法处理)
我只是负责写代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
struct edge{
int to,w,next;
}p[20010];
int head[10010];
int tot;
void addedge(int a,int b,int w){
p[tot].to=b;
p[tot].w=w;
p[tot].next=head[a];
head[a]=tot++;
}
struct line{
int u,v,w;
line(){}
line(int uu,int vv,int ww):u(uu),v(vv),w(ww){}
bool operator<(const line &a)const{
return w>a.w;
}
}q[50010];
int fa[10010];
int findset(int x){
return fa[x]==x?x:(fa[x]=findset(fa[x]));
}
void unionset(int a,int b){
fa[findset(a)]=findset(b);
}
int f[10010][20];
int d[10010][20];
int dep[10010];
int vis[10010];
void dfs(int u,int fa){
f[u][0]=fa;
dep[u]=dep[fa]+1;
vis[u]=1;
for(int i=head[u];i!=-1;i=p[i].next){
int v=p[i].to;
if(v!=fa){
d[v][0]=p[i].w;
dfs(v,u);
}
}
}
void init(int n){
memset(vis,0,sizeof vis);
memset(d,0x3f,sizeof d);
for(int i=1;i<=n;i++)
if(!vis[i]) {dep[i]=0;dfs(i,0);}
for(int j=0;j<18;j++)
for(int i=1;i<=n;i++)
if(!f[i][j]) {f[i][j+1]=0;d[i][j+1]=0;}
else{
f[i][j+1]=f[f[i][j]][j];
d[i][j+1]=min(d[i][j],d[f[i][j]][j]);
}
}
int LCA(int u,int v){
int ans=0x3f3f3f3f;
if(dep[u]<dep[v]) swap(u,v);
int df=dep[u]-dep[v],t=0;
while(df){
if(df&1){
ans=min(ans,d[u][t]);
u=f[u][t];
}
t++;
df>>=1;
}
if(u==v) return ans;
for(int i=18;i>=0;i--)
if(f[u][i]!=f[v][i]){
ans=min(ans,d[u][i]);
ans=min(ans,d[v][i]);
u=f[u][i];
v=f[v][i];
}
return min(ans,min(d[u][0],d[v][0]));
}
int main()
{
int n,m,a,b,c;
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
scanf("%d%d%d",&a,&b,&c);
q[i]=line(a,b,c);
}
sort(q,q+m);
for(int i=1;i<=n;i++) fa[i]=i;
memset(head,-1,sizeof head);
tot=0;
int cnt=0;
for(int i=0;i<m;i++){ //kruskal
int u=q[i].u,v=q[i].v;
if(findset(u)==findset(v)) continue;
unionset(u,v);
addedge(u,v,q[i].w);
addedge(v,u,q[i].w);
if(++cnt==n-1) break;
}
init(n);
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&a,&b);
if(findset(a)!=findset(b)) puts("-1");
else printf("%d\n",LCA(a,b));
}
return 0;
}