[洛谷P1967 货车运输][((树链剖分+线段树)/克鲁斯卡尔重构树)+LCA]

https://www.luogu.org/problemnew/show/P1967
题目描述
AA国有n n座城市,编号从 1 1到 nn,城市之间有 mm 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 qq 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入输出格式
输入格式:
第一行有两个用一个空格隔开的整数 n,mn,m,表示 AA 国有 nn 座城市和 mm 条道路。

接下来 mm行每行 3 3个整数 x, y, zx,y,z,每两个整数之间用一个空格隔开,表示从 x x号城市到 y y号城市有一条限重为 zz 的道路。注意: xx 不等于 yy,两座城市之间可能有多条道路 。

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。

输出格式:
共有 qq 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1−1。

输入输出样例
输入样例#1:
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
输出样例#1:
3
-1
3
说明
对于 30%30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,0000<n<1,000,0<m<10,000,0<q<1,000;

对于 60%60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,0000<n<1,000,0<m<50,000,0<q<1,000;

对于 100%100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,0000<n<10,000,0<m<50,000,0<q<30,000,0≤z≤100,000。

题意:有一个图,q次询问,每次询问两个点u,v,求出从u-v路径边权最小值的最大值
题解:可以知道很多边是永远不会经过的,可按照边权排序之后重构一棵树,然后就是寻找路径上边权的最小值,此时容易想到树链剖分+线段树维护,这是解法一。另一种解法是克鲁斯卡尔重构树。克鲁斯卡尔树就是在边排序之后,重构连边的时候不直接u和v相连,而新建一个节点作为u集合的代表点和v集合的代表点的父节点,并且把这个新节点作为uv集合的代表点,由于是排序,所以父节点一定不小于(或者不大于)子树任意节点,所以求一个路径上的最值就相当于求两个端点的LCA。

树链剖分+线段树

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" is "<<x<<endl;
const int maxn=5e4+5;
const int maxn2=1e4+5;
struct edge{
    int fr;
    int to;
    int nex;
    int val;
}e[maxn<<1],e2[maxn<<1];
struct node{
    int l;
    int r;
    int minn;
}Node[maxn2<<2];
int cnt,cnt2,head[maxn2],fa[maxn2],id[maxn2],fa2[maxn2],flag[maxn<<1],siz[maxn2],depth[maxn2],acc[maxn2],dfn[maxn2],son[maxn2],top[maxn2],tim;
void adde1(int x,int y,int z){
    e[cnt2].fr=x;
    e[cnt2].to=y;
    e[cnt2].val=z;
    cnt2++;
}
void adde(int x,int y,int z){
    e2[cnt].fr=x;
    e2[cnt].to=y;
    e2[cnt].val=z;
    e2[cnt].nex=head[x];
    head[x]=cnt++;
}
bool cmp(struct edge xx,struct edge yy){
    return xx.val>yy.val;
}
void dfs1(int u,int f){
    fa[u]=f;
    siz[u]=1;
    for(int i=head[u];i!=-1;i=e2[i].nex){
        int v=e2[i].to;
        if(v==f)continue;
        depth[v]=depth[u]+1;
        acc[v]=e2[i].val;
        dfs1(v,u);
        siz[u]+=siz[v];
        if(siz[son[u]]<siz[v]){
            son[u]=v;
        }
    }
}
void dfs2(int u,int f,int topp){
    top[u]=topp;
    dfn[u]=++tim;
    id[tim]=u;
    if(son[u]){
        dfs2(son[u],u,topp);
    }
    for(int i=head[u];i!=-1;i=e2[i].nex){
        int v=e2[i].to;
        if(v==f||v==son[u])continue;
        dfs2(v,u,v);
    }
}
int finds(int x){
    int xx=x;
    while(fa2[x]!=x){
        x=fa2[x];
    }
    while(fa2[xx]!=x){
        int t=fa2[xx];
        fa2[xx]=x;
        xx=t;
    }
    return x;
}
void pushup(int rt){
    Node[rt].minn=min(Node[rt<<1].minn,Node[(rt<<1)|1].minn);
}
void build(int L,int R,int rt){
    Node[rt].l=L;
    Node[rt].r=R;
    if(L==R){
        Node[rt].minn=acc[id[L]];
        return;
    }
    int mid=(L+R)/2;
    build(L,mid,rt<<1);
    build(mid+1,R,(rt<<1)|1);
    pushup(rt);
}
int query(int ll,int rr,int rt,int lll,int rrr){
    if(ll>=lll&&rr<=rrr)return Node[rt].minn;
    int minx=0x3f3f3f3f;
    int mid=(ll+rr)/2;
    if(mid>=lll)minx=min(minx,query(ll,mid,rt<<1,lll,rrr));
    if(mid<rrr)minx=min(minx,query(mid+1,rr,(rt<<1)|1,lll,rrr));
    return minx;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)fa2[i]=i;
    for(int i=1;i<=m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        adde1(a,b,c);
    }
    sort(e,e+cnt2,cmp);
    for(int i=0;i<cnt2;i++){
        int f1=finds(e[i].fr);
        int f2=finds(e[i].to);
        if(f1!=f2){
            fa2[f1]=f2;
            adde(e[i].fr,e[i].to,e[i].val);
            adde(e[i].to,e[i].fr,e[i].val);
        }
    }
    for(int i=1;i<=n;i++){
        if(!siz[i])dfs1(i,0);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i])dfs2(i,0,i);
    }
    build(1,tim,1);
    int q;
    scanf("%d",&q);
    while(q--){
        int a,b;
        scanf("%d%d",&a,&b);
        int minn=0x3f3f3f3f;
        int f1=finds(a);
        int f2=finds(b);
        if(f1!=f2){printf("-1\n");continue;}
        while(top[a]!=top[b]){
            if(depth[top[a]]<depth[top[b]]){
                swap(a,b);
            }
            if(dfn[top[a]]<=dfn[a])minn=min(query(1,tim,1,dfn[top[a]],dfn[a]),minn);
            a=fa[top[a]];
        }
        if(depth[b]<depth[a])swap(a,b);
        if(dfn[a]+1<=dfn[b])minn=min(query(1,tim,1,dfn[a]+1,dfn[b]),minn);
        if(minn==0x3f3f3f3f)minn=-1;
        printf("%d\n",minn);
    }
    return 0;
}

克鲁斯卡尔重构树+LCA

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" is "<<x<<endl;
const int maxn=1e4+5;
const int maxn2=5e4+5;
struct edge{
    int to;
    int nex;
}e[maxn<<2];
struct edge2{
    int fr;
    int to;
    int val;
}e2[maxn2];
int head[maxn<<2],fa[maxn<<2],depth[maxn<<2],acc[maxn<<2],fa2[maxn<<2][31],cnt;
bool cmp(struct edge2 aa,struct edge2 bb){
    return aa.val>bb.val;
}
void adde(int x,int y){
    e[cnt].to=y;
    e[cnt].nex=head[x];
    head[x]=cnt++;
}
void dfs(int u,int f){
    fa2[u][0]=f;
    for(int i=head[u];i!=-1;i=e[i].nex){
        int v=e[i].to;
        if(v==f)continue;
        depth[v]=depth[u]+1;
        dfs(v,u);
    }
}
int finds(int x){
    int xx=x;
    while(fa[x]!=x){
        x=fa[x];
    }
    while(fa[xx]!=x){
        int t=fa[xx];
        fa[xx]=x;
        xx=t;
    }
    return x;
}
int main(){
    memset(head,-1,sizeof(head));
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n*2;i++)fa[i]=i;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&e2[i].fr,&e2[i].to,&e2[i].val);
    }
    sort(e2+1,e2+1+m,cmp);
    int ac=0;
    for(int i=1;i<=m;i++){
        int f1=finds(e2[i].fr);
        int f2=finds(e2[i].to);
        if(f1!=f2){
            ac++;
            adde(f1,n+ac);
            adde(n+ac,f1);
            adde(f2,n+ac);
            adde(n+ac,f2);
            acc[n+ac]=e2[i].val;
            fa[f1]=n+ac;
            fa[f2]=n+ac;
        }
    }
    for(int i=n+ac;i>=1;i--){
        if(fa2[i][0]==0)dfs(i,0);
    }
    for(int i=1;i<31;i++){
        for(int j=1;j<=n+ac;j++){
            fa2[j][i]=fa2[fa2[j][i-1]][i-1];
        }
    }
    int q;
    scanf("%d",&q);
    while(q--){
        int a,b;
        scanf("%d%d",&a,&b);
        int f1=finds(a);
        int f2=finds(b);
        if(f1!=f2){printf("-1\n");continue;}
        if(depth[a]<depth[b])swap(a,b);
        int acw=depth[a]-depth[b];
        for(int i=0;i<31;i++){
            if(acw&(1ll<<i)){
                a=fa2[a][i];
            }
        }
        for(int i=30;i>=0;i--){
            if(fa2[a][i]!=fa2[b][i]){
                a=fa2[a][i];
                b=fa2[b][i];
            }
        }
        printf("%d\n",acc[fa2[a][0]]);
    }
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值