NOIP2013货车运输 计蒜客习题 蒜头君运送宝藏

手画样例容易发现每次询问所求答案为路径上的最小边

而最大生成树的最小边一定是所有生成树中最大的 且原图的连通性不变

指定每个连通块中的任意一个结点为根建树  可以发现每次询问的货车路径经过他们的LCA

可以在求LCA的同时维护最小边的信息 同样用一个倍增数组 而判断连通可以用并查集

半个周日都献给这题了丂

点击打开链接

#include<bits/stdc++.h>
using namespace std;
const int maxn=10001,maxm=50001,inf=0x3f3f3f3f;
struct e{int u,v,w; bool operator<(const e &a)const{return w>a.w;}}t[maxm];
struct edge{int v,w,nxt;}e[maxm<<1];
int p[maxn],fa[maxn],eid=0,anc[maxn][20],w[maxn][20],d[maxn],n,m,q,rst;
inline int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
inline void dfs(int u){
    for(int i=p[u];~i;i=e[i].nxt){
        int v=e[i].v;
        if(!d[v]){
            d[v]=d[u]+1;
            anc[v][0]=u;
            w[v][0]=e[i].w;
            dfs(v);
        }
    }
}
inline void pre(){
    memset(p,-1,sizeof(p));
    for(int i=1;i<=n;i++) fa[i]=i;
}
inline void init(){    
    for(int lv=1;(1<<lv)<=n;lv++)
    {
        for(int i=1;i<=n;i++)
        {
            anc[i][lv]=anc[anc[i][lv-1]][lv-1];
            w[i][lv]=min(w[anc[i][lv-1]][lv-1],w[i][lv-1]);//倍增维护最小值
        }
    }
}
inline int lca(int x,int y){
    int ans=inf;
    if(d[x]<d[y]) swap(x,y);
    int i,j;
    for(i=0;(1<<i)<=d[x];i++);
    i--;
    for(int j=i;j>=0;j--)
    {
        if(d[x]-d[y]>=(1<<j)){
            ans=min(ans,w[x][j]);   //往上跳并更新w[x][j]
            x=anc[x][j];
        }
    }
    if(x==y) return ans;
    for(j=i;j>=0;j--){
        if(anc[x][j]!=anc[y][j])
        {
            ans=min(ans,min(w[x][j],w[y][j]));  //同时两个往上跳
            x=anc[x][j];
            y=anc[y][j];
        }
    }
    ans=min(ans,min(w[x][0],w[y][0]));  //一步到lca
    return ans;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&t[i].u,&t[i].v,&t[i].w);
    }
    sort(t+1,t+m+1);
    pre();
    rst=n;
    for(int i=1;i<=m;i++)
    {
        int u=t[i].u,v=t[i].v,w=t[i].w;
        int x=get(u),y=get(v);
        if(x!=y){
            fa[x]=y;
            rst--;
            e[eid].v=v;e[eid].w=w;e[eid].nxt=p[u];p[u]=eid++;
            e[eid].v=u;e[eid].w=w;e[eid].nxt=p[v];p[v]=eid++;
            if(rst==1) break;   //建图
        }
    }
    for(int i=1;i<=n;i++)
        if(d[i]==0){
            d[i]=1;
            anc[i][0]=0;
            dfs(i);
        }
    init();
    cin>>q;
    int a,b;
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&a,&b);
        if(get(a)!=get(b)){
            puts("-1");
        }
        else{
            printf("%d\n",lca(a,b));
        } 
    }
    return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值