每日一题 P1967 [NOIP2013 提高组] 货车运输 最大生成树 LCA倍增

每日一题 P1967 [NOIP2013 提高组] 货车运输 最大生成树 LCA倍增


这道题虽然是蓝色题,但是其实不难想,代码量有点大。debug了很久,还是wa,最后发现又是小错误。

先用DSU+Kruskal跑最大生成树,然后用LCA倍增的方式,处理出一个点往上2^i步的点和这两个点之间所有边的最小值,然后就可以很方便的改改倍增的板子来求解了。

对板子的熟悉程度不够,导致了一些小bug,最后是对着别人代码看找出来的问题。

#include <bits/stdc++.h>
#define MAXN 500005
using namespace std;
struct EDGE
{
    int to,next,w;
}edge[MAXN];int ptr;
int head[MAXN];
void add_edge(int u,int v,int w)
{
    edge[++ptr].to=v;
    edge[ptr].w=w;
    edge[ptr].next=head[u];
    head[u]=ptr;
}
void add(int a,int b,int w)
{
    add_edge(a,b,w);
    add_edge(b,a,w);
}

int dsu[MAXN];int n,m;
void init()
{
    for(int i=0;i<MAXN;i++)
        dsu[i]=i;
}
int fnd(int x)
{
    if(dsu[x]==x)return x;
    return dsu[x]=fnd(dsu[x]);
}
void merg(int x,int y)
{
    dsu[fnd(x)]=fnd(y);
}
struct E
{
    int a,b,w;
};
bool cmp(E a,E b)
{
    return a.w>b.w;
}
vector<E> v;
void kruskal()
{
    sort(v.begin(),v.end(),cmp);
    for(int i=0;i<v.size();i++)
    {
        if(fnd(v[i].a)==fnd(v[i].b))continue;
        add(v[i].a,v[i].b,v[i].w);
        merg(v[i].a,v[i].b);
    }
}
int dep[MAXN],f[MAXN][25],me[MAXN][25];
void dfs(int now,int fa)
{
    for(int p=head[now]; p; p=edge[p].next)
    {
        int to=edge[p].to;
        if(to==fa)continue;
        dep[edge[p].to]=dep[now]+1;//计算深度
        f[to][0]=now;
        me[to][0]=edge[p].w;
        dfs(to,now);
    }
}
void dp()
{
    //预处理 f[u][i] u结点往上走2^i次
    for(int i=1; (1<<i)<=n; i++)
        for(int u=1; u<=n; u++)
            f[u][i]=f[f[u][i-1]][i-1],me[u][i]=min(me[u][i-1],me[f[u][i-1]][i-1]);//u结点往上走2^i次等于网上走两个2^(i-1)
}
int lca(int x,int y)
{
    int ret=INT_MAX;
    int p,t;
    if(dep[x]<dep[y])
        swap(x,y);//保证x比y大
    for(p=0; (1<<p)<=dep[x]; p++);//算出dp最多往上走2的多少次
    for(t=--p; t>=0; t--)//从x走到y同一层
        if(dep[x]-(1<<t)>=dep[y])//不超过的话就往上走
            ret=min(ret,me[x][t]),x=f[x][t];
    if(x==y)
        return ret;
    for(t=p; t>=0; t--)//x和y一起走 不相等就往上走
        if(f[x][t]!=f[y][t])
            ret=min(ret,me[x][t]),x=f[x][t],ret=min(ret,me[y][t]),y=f[y][t];
    return min(ret,min(me[x][0],me[y][0]));
}
int main()
{
    init();
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b,w;cin>>a>>b>>w;
        v.push_back(E{a,b,w});
    }
    kruskal();
    for(int i=1;i<=n;i++)
        if(!dep[i])
            dfs(i,0);
    dp();
    int q;cin>>q;
    while(q--)
    {
        int a,b;cin>>a>>b;
        if(fnd(a)!=fnd(b))cout<<-1<<endl;
        else cout<<lca(a,b)<<endl;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值