P1967 货车运输(Tarjan做法)

9 篇文章 0 订阅
9 篇文章 0 订阅

传送门
这个问题叫做『最大瓶颈路径』
最大瓶颈路径一定存在于最大生成树中/反证法:如果最大瓶颈路径不存在与最大生成树中。
这些不在最大生成树中的边会和最大生成树形成环。
我们删掉环上最小的边,保留这一条边,会得到一棵新的更大的生成树。
这与原来那棵树是最大生成树矛盾了。
注意,最短路不一定在最小生成树上(如一个环的情况)


现在问题转变为求树上两点的最小边权问题了。
很明显是从u->LCA(u,v)->v这条路能使最小边权最大。
我们先用Tarjan求出LCA.记录下来。
用一个数组记录DFS遍历的前一个节点和边权,然后递归分别找u->LCA(u,v)的最小权值,v->LCA(u,v)的最小权值
两者取min即为答案。
需要注意的是,加入询问的两个点不在一颗树上,Tarjan跑出的LCA其实是不对的,所以要在最后输出时去判断一下是否在一棵树中。

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxm=510000;
struct Node{
    int net;
    int to;
    int cost;
    int lca;
    int ans;
};
bool vis[maxm];
Node edge[2][2*maxm];
int cnt[2],head[2][maxm],n,m;
int fatt[maxm],fat[maxm],dis[maxm]; 
int Dis1,Dis2;
int ansx[maxm],ansy[maxm];
int fatx[maxm];
void add(int id,int x,int y,int c)
{
    edge[id][++cnt[id]].to=y;
    edge[id][cnt[id]].cost=c;
    edge[id][cnt[id]].net=head[id][x];
    head[id][x]=cnt[id];
}
struct node{
    int x,y,z;
}a[maxm]; 
int find1(int x)
{
    if(x==fatt[x]) return x;
    return fatt[x]=find1(fatt[x]);
}
int findx(int x)
{
    if(x==fatx[x]) return x;
    else
     return fatx[x]=findx(fatx[x]);
}
int find(int id,int x,int fs)
{
    if(id==0)
    {
     Dis1=1e17;
     while(fat[x]!=x&&fs!=x)
     {
        Dis1=min(Dis1,dis[x]);
        x=fat[x];
     }
     return x;
    }
    else
    {
     Dis2=1e17;
     while(fat[x]!=x&&fs!=x)
     {
        Dis2=min(Dis2,dis[x]);
        x=fat[x];
     }
     return x;
    }
}
bool comp(const node&x,const node&y)
{
    return x.z>y.z;
}
void dfs(int x)
{
    vis[x]=1;
    fat[x]=x;
    fatx[x]=x;
    for(int K=head[0][x];K;K=edge[0][K].net)
    {
        int p=edge[0][K].to;
        if(vis[p]) continue;
        dfs(p);
        fatx[p]=x;
        fat[p]=x;
        dis[p]=edge[0][K].cost;
    }

    for(int i=head[1][x];i;i=edge[1][i].net)
    {
        int p=edge[1][i].to;
        if(!vis[p]) continue;

        //int ans=min(Dis1,Dis2);
        //edge[1][i].ans=ans;
        edge[1][i].lca=findx(p);
        ansx[i]=x,ansy[i]=p;
        if(i%2)
        edge[1][i+1].lca=edge[1][i].lca,ansx[i+1]=x,ansy[i+1]=p;
        else
        edge[1][i-1].lca=edge[1][i].lca,ansx[i-1]=x,ansy[i-1]=p;

    }
}
int main()
{
    //freopen("truck.in","r",stdin);
    //freopen("truck.out","w",stdout);
    int maxf=0;
    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;i++) fatt[i]=i,fat[i]=i,fatx[i]=i;
    for(int i=1,x,y,z;i<=m;i++)
     scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);

    sort(a+1,a+m+1,comp);
    int tot=0;
    for(int i=1;i<=m&&tot!=n-1;i++)
    if(find1(a[i].x)!=find1(a[i].y))
    {
        fatt[find1(a[i].x)]=find1(a[i].y);
        add(0,a[i].x,a[i].y,a[i].z);add(0,a[i].y,a[i].x,a[i].z);
        tot++;
    }
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(1,x,y,0),add(1,y,x,0);
    }
    for(int i=1;i<=n;i++)
     if(!vis[i])
      dfs(i);
    for(int i=1;i<=t;i++)
    if(findx(ansx[2*i])!=findx(ansy[2*i]))
      printf("-1\n");
    else
    {
        find(0,ansx[2*i],edge[1][2*i].lca);
        find(1,ansy[2*i],edge[1][2*i].lca);
        printf("%d\n",min(Dis1,Dis2));
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值