原题链接:
[NOIP2013 提高组] 货车运输 - 洛谷https://www.luogu.com.cn/problem/P1967
题目描述
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。
现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入格式
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行三个整数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
输出 #1复制
3 -1 3
说明/提示
对于 100% 的数据,1≤n<10^4,1≤m<5×10^4,1≤q<3×10^4,0≤z≤105。
Solution:
对于本题,可以发现边权小的路段永远不会走过,因为需要载重最大值,可以看出使用最大生成树
这里使用kruskal,这里大家很容易想到一种暴力解法,将边按权值从大到小排序就是对于最大生成树的每一次合并,都对其所有的起点和终点都进行一次判断联通性,已经联通了的打上标记,对未联通的进行一次判断联通性,如果在该边在本次连边中联通了,那么该边权就是答案
时间复杂度(nq)只能拿60分。
60 opts code:
#include<bits/stdc++.h>
using namespace std;
struct node{
int u,v,w;
}e[100005];
int n,m,q;
int st[30005],ed[30005];
int vis[30005];
int ans[30005];
int fa[100005];
bool cmp(node a,node b){return a.w>b.w;}
int find(int x){return x==fa[x]?x:fa[x] = find(fa[x]);}
void uni(int x,int y){fa[find(x)] = find(y);}
bool flag = true;
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i = 1;i<=n;++i) fa[i] = i;
for(int i = 1;i<=m;++i)
cin>>e[i].u>>e[i].v>>e[i].w;
sort(e+1,e+1+m,cmp);
cin>>q;
memset(ans,-1,sizeof(ans));
for(int i = 1;i<=q;++i)
cin>>st[i]>>ed[i];
for(int i = 1;i<=m;++i)
{
if(n==1) break;
if(find(e[i].u)!=find(e[i].v))
{
uni(e[i].u,e[i].v);
n--;
bool flag = true;
for(int j = 1;j<=q;++j)
{
if(vis[j]) continue;
if(find(st[j])==find(ed[j]))
ans[j] = e[i].w,vis[j] = 1;
flag = false;
}
if(flag) break;
}
}
for(int i = 1;i<=q;++i)
cout<<ans[i]<<endl;
return 0;
}
所以必须想到优化,那么就只能先将整个树连完了再说,做完了最大生成树,我们可以发现一个性质,树的a点到树的b点有且只有一条路径,那是什么呢?就是a点和b点lca过程走过的路径,那么我们可以在求lca的过程中统计最小边权,最后返回
可以在lca预处理的过程中增加一个w数组,w[x][i]为x与其第2^i个祖先的最小边权,状态转移与fa的转移类似:w[x][i] = min(w[x][i-1],w[fa[x][i-1]][i-1])
那么这道问题,就愉快的解决啦~
ACcode:
#include<bits/stdc++.h>
using namespace std;
struct node{
int u,v,w;
}e[100005];
struct ee{
int v,w;
ee(){}
ee(int v1,int w1):v(v1),w(w1){}
};
vector<ee> edge[100005];
int n,m,q;
int st[30005],ed[30005];
int vis[30005];
int ans[30005];
int fa[100005];
int lg[100005];
int deep[100005];
int fath[100005][25];
int w[100005][25];
bool cmp(node a,node b){return a.w>b.w;}
int find(int x){return x==fa[x]?x:fa[x] = find(fa[x]);}
void uni(int x,int y){fa[find(x)] = find(y);}
void dfs(int x,int ff,int num)
{
deep[x] = deep[ff]+1;
fath[x][0] = ff;
w[x][0] = num;
for(int i = 1;i<=lg[deep[x]-1];++i)
{
fath[x][i] = fath[fath[x][i-1]][i-1];
w[x][i] = min(w[x][i-1],w[fath[x][i-1]][i-1]);
}
for(int i = 0;i<edge[x].size();++i)
if(edge[x][i].v!=ff) dfs(edge[x][i].v,x,edge[x][i].w);
}
int lca(int x,int y)
{
if(find(x)!=find(y)) return -1;
if(deep[x]<deep[y])
swap(x,y);
int ans = 0x3f3f3f3f;
while(deep[x]>deep[y])
{
ans = min(ans,w[x][lg[deep[x]-deep[y]]-1]);
x = fath[x][lg[deep[x]-deep[y]]-1];
}
if(x==y) return ans;
for(int k = lg[deep[x]-1];k>=0;k--)
if(fath[x][k]!=fath[y][k])
{
ans = min(ans,w[x][k]);
ans = min(ans,w[y][k]);
x = fath[x][k];
y = fath[y][k];
}
ans = min(ans,w[x][0]);
ans = min(ans,w[y][0]);
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i = 1;i<=n;++i) fa[i] = i;
for(int i = 1;i<=m;++i)
cin>>e[i].u>>e[i].v>>e[i].w;
sort(e+1,e+1+m,cmp);
cin>>q;
int cnt = 0;
for(int i = 1;i<=m;++i)
{
if(cnt==n-1) break;
if(find(e[i].u)!=find(e[i].v))
{
uni(e[i].u,e[i].v);
edge[e[i].u].push_back(ee(e[i].v,e[i].w));
edge[e[i].v].push_back(ee(e[i].u,e[i].w));
cnt++;
}
}
for(int i = 1;i<=n;++i)
lg[i] = lg[i-1]+(1<<lg[i-1]==i);
dfs(1,0,-1);
for(int i = 1;i<=q;++i)
{
int x,y;
cin>>x>>y;
cout<<lca(x,y)<<endl;
}
return 0;
}