【LCA && 题解】 洛谷1967 货车运输

题目传送门

题目描述
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。


分析:因为两个点之间的距离是路径中的最小的权值,想要这个权值最大,那么就是最小的最大,这时我们想到了最大生成树。我们用跑最大生成树重新建图,然后在新图上跑倍增LCA记录权值和所对应的父亲即可。

我们尝试用 f a [ i ] [ j ] fa[i][j] 表示 第i个点以上 2 j 2^j 辈的点是什么,再用 m o [ i ] [ j ] mo[i][j] 表示第i个点向上 2 j 2^j 辈的最小权值是什么(最小的最大)

我们不难推出以下公式:

f a [ i ] [ j ] = f a [ f a [ i ] [ j 1 ] ] [ j 1 ] fa[i][j]=fa[fa[i][j-1]][j-1]
m o [ i ] [ j ] = m i n ( m o [ i ] [ j 1 ] , m o [ f a [ i ] [ j 1 ] ] [ j 1 ] ) mo[i][j]=min(mo[i][j-1],mo[fa[i][j-1]][j-1])

我们只需要预处理出这两个公式在树上跑一遍倍增求LCA即可

那么具体代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int fa[100010][20];
int f[100010];
bool vis[100010];
int mo[100010][20];
struct node{
    int x,y,v;
}e1[1000010];
struct node1{
    int y,Next,v;
}e[1000100];
int cnt=0,len=0;
int linkk[100010];
int d[100010];
int d1[100010];
bool mycmp(node x,node y){
    return x.v>y.v;
}
int getfa(int k){
    return k==f[k]?k:f[k]=getfa(f[k]); 
}
void insert(int x,int y,int v){
    e[++len].Next=linkk[x];
    linkk[x]=len;
    e[len].y=y;
    e[len].v=v;
}
void dfs(int x,int de){
	if (vis[x]) return;
	vis[x]=1;
    d[x]=de;
    for (int i=linkk[x];i;i=e[i].Next){
	    int y=e[i].y;
	    if (y==fa[x][0]) continue;
	    fa[y][0]=x;
	    mo[y][0]=e[i].v;
	    dfs(y,de+1);
	}
}
void find_fa(){
    for (int j=1;j<=19;j++)
      for (int i=1;i<=n;i++)
         fa[i][j]=fa[fa[i][j-1]][j-1],mo[i][j]=min(mo[i][j-1],mo[fa[i][j-1]][j-1]);
}
int lca(int u,int v){
	if (getfa(u)!=getfa(v)) return -1;
	int ans=10000000;
    if (d[u]>d[v]) swap(u,v);
    for (int i=19;i>=0;i--)
      if (d[fa[v][i]]>=d[u]) ans=min(ans,mo[v][i]),v=fa[v][i];
    if (u==v) return ans;
    for (int i=19;i>=0;i--)
      if (fa[u][i]!=fa[v][i]) ans=min(ans,min(mo[u][i],mo[v][i])),u=fa[u][i],v=fa[v][i];
    return min(ans,min(mo[u][0],mo[v][0]));
}
int main(){
    scanf("%d %d",&n,&m);
    for (int i=1,x,y,z;i<=m;i++) scanf("%d %d %d",&x,&y,&z),e1[++cnt]=(node){x,y,z},e1[++cnt]=(node){y,x,z};
    sort(e1+1,e1+cnt+1,mycmp);
    for (int i=1;i<=n;i++) f[i]=i;
    for (int i=1;i<=cnt;i++){
	    int x=getfa(e1[i].x),y=getfa(e1[i].y);
	    if (x!=y)
	      f[x]=y,insert(x,y,e1[i].v),insert(y,x,e1[i].v);//最大生成树建边
	}
	int root=1;
    for (int i=1;i<=n;i++)
      if (!vis[i]){
      	  d[i]=1;
	      dfs(i,1);
	      fa[i][0]=i;
	      mo[i][0]=10000000;
	  }//可能是森林,搜索未搜索的点,并计算出深度 
	find_fa();//预处理出fa数组 
	int q;
	scanf("%d",&q);
	for (int i=1,x,y;i<=q;i++) scanf("%d %d",&x,&y),printf("%d\n",lca(x,y));//输出它们最近公共祖先的权值,此时即最小的最大载重 
}

猜你喜欢

转载自blog.csdn.net/huang_ke_hai/article/details/87334427