UVA-11354-Bond(树上倍增,dp,MST,LCA)

5 篇文章 0 订阅
4 篇文章 0 订阅

题目链接:UVA-11354-Bond

首先,最大边最小的路径称为最小瓶颈路,最小瓶颈路一定是最小生成树上的路径(根据Kruskal)。
因此先处理处最小生成树,然后将最小生成树转化成一棵有根树。
anc[i][j] 为点 i 的第 2j 层祖先,如: anc[i][0]=fa[i]
cost[i][j] 为点 i anc[i][j] 的路径上的最大边的权值。
状态转移方程为:
anc[i][j]=anc[anc[i][j1]][j1]
cost[i][j]=max(cost[i][j1],cost[anc[i][j1]][j1])
处理出 anc cost 后,就可以很方便地查询了。
当查询 (u,v) 时,设 deep[u]>deep[v]
我们先把 u 提升到与 v 的同一层。
使用倍增即可。
然后将 u v 共同提升,使得 u v 均成为 LCA(u,v) 的儿子。
在提升的过程中更新 ans 即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+7;
int n,m;
int fa[maxn];
int cost[maxn][20],anc[maxn][20],deep[maxn];
int k;
struct Edge
{
    int u,v,d;
    Edge(){}
    Edge(int _v,int _d):v(_v),d(_d){}
    bool operator < (const Edge & r) const
    {
        return d<r.d;
    }
};
Edge e[maxn<<1];
vector<Edge> adj[maxn];
int f(int x)
{
    return x==fa[x]?x:fa[x]=f(fa[x]);
}
bool Union(int x,int y)
{
    x=f(x);y=f(y);
    if(x==y) return false;
    fa[x]=y;
    return true;
}
void addedge(int u, int v, int d)
{
    adj[u].push_back(Edge(v,d));
    adj[v].push_back(Edge(u,d));
}
void dfs(int u,int pre,int deep)
{
    ::deep[u]=deep;
    anc[u][0]=pre;
    for(int i=0;i<adj[u].size();i++)
    {
        int v=adj[u][i].v;
        if(v==pre) continue;
        cost[v][0]=adj[u][i].d;
        dfs(v,u,deep+1);
    }
}
void init()
{
    cost[1][0]=0;
    k=0;
    for(int j=1;(1<<j)<n;j++)
    {
        k=j;
        for(int i=1;i<=n;i++)
        {
            anc[i][j]=-1;
            int p=anc[i][j-1];
            if(p==-1) continue;
            anc[i][j]=anc[p][j-1];
            cost[i][j]=max(cost[i][j-1],cost[p][j-1]);
        }
    }
}
int solve(int u,int v)
{
    if(deep[u]<deep[v]) swap(u,v);
    int ans=-1;
    for(int i=k;i>=0;i--)
    {
        if(deep[u]-(1<<i)>=deep[v])
        {
            ans=max(ans,cost[u][i]);
            u=anc[u][i];
        }
    }
    if(u==v) return ans;
    for(int i=k;i>=0;i--)
    {
        if(anc[u][i]!=-1&&anc[u][i]!=anc[v][i])
        {
            ans=max(ans,cost[u][i]);
            ans=max(ans,cost[v][i]);
            u=anc[u][i];
            v=anc[v][i];
        }
    }
    ans=max(ans,cost[u][0]);
    ans=max(ans,cost[v][0]);
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    int s,t;
    bool flag=false;
    while(cin>>n>>m)
    {
        if(flag) cout << endl;
        flag=true;
        int u,v,d;
        for(int i=0;i<m;i++)
            cin>>e[i].u>>e[i].v>>e[i].d;
        sort(e,e+m);
        for(int i=1;i<=n;i++) adj[i].clear();
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=0;i<m;i++)
        {
            if(Union(e[i].u,e[i].v))
                addedge(e[i].u,e[i].v,e[i].d);
        }
        dfs(1,-1,0);
        init();
        int q;
        cin>>q;
        while(q--)
        {
            cin>>u>>v;
            cout << solve(u,v) << endl;
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值