洛谷题解 UVA11354 【Bond】 瓶颈树+kruskal+最小公共祖先

题目跟2245是类似的。
主要用到了最小生成树求最大值,因为最小生成树是从小到大排序的。
排序用到了克里斯卡尔的算法。

用LCA算法求出最近公共祖先,再在求最近公共祖先的路上,一直取最大的值。就成功得到了最小生成树中的最大值

//prim没试过。应该可以


```
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
#define inf 0x3f3f3f3f;
int depth[N],n,m,cnt,fa[N][30],fi[N],ba[N],w[N][30],q,mst;
bool isv[N];//isv访问 depth深度 cnt不想解释。。

//fa倍增保存祖先 ba求父亲(爸) w存最大边权值 mst是MST算法。。

struct Mo
{
    int from,to,w;
    bool operator<(Mo a)const{return w<a.w;}//重载取最小生成树
}e[N];//存边


struct li
{
    int to,w,nxt;
}eg[N];//用来连接


inline int find(register int x)
{
    return ba[x]=(ba[x]==x?x:find(ba[x]));
}//路径压缩加找父亲


inline void add(register int x,register int y,register int w)
{
    eg[++cnt].to=y,eg[cnt].w=w,eg[cnt].nxt=fi[x];
    fi[x]=cnt;
}//链式前向星的添加


inline int read()
{
    register int r=0,f=1;
    register char e=getchar();
    while(!isdigit(e))
    {
        if(e=='-')
        f=-1;
        e=getchar();
    }
    while(isdigit(e))
    r=(r<<1)+(r<<3)-'0'+e,e=getchar();
    return f*r;
}//快读,这个比较有用


inline void out(register int x)
{
    if(x>9)
    out(x/10);
    putchar(x%10+'0');
}//快输,其实这个没啥效率


inline void init()
{
    memset(fi,0,sizeof(fi));
    cnt=0,mst=0;
    memset(isv,0,sizeof(isv));
}//初始化


inline void kruskal()
{
    sort(e+1,e+1+m);//调用排序
    for(register int i=1;i<=m;i++)
    {
        register int x=find(e[i].from),y=find(e[i].to);
        if(x!=y)
        {
            ba[x]=y;
            add(e[i].from,e[i].to,e[i].w);
            add(e[i].to,e[i].from,e[i].w);//双向链接,用前向星。
            mst++;
            if(mst==n-1)break;//好像不加这个限制也是可以过的
        }
    }
}//求最小生成树


inline void dfs(int x)
{
    isv[x]=true;
    for(register int i=fi[x];i;i=eg[i].nxt)
    {
        register int to=eg[i].to;
        if(isv[to])continue;//被访问过就继续
        depth[to]=depth[x]+1;//否则深度加1
        fa[to][0]=x;//保存父亲
        w[to][0]=eg[i].w;//保存父亲权值
        dfs(to);
    }
    return ;
}//搜索计算深度,初始化fa,w


inline int LCA(register int x,register int y)
{
    if(find(x)!=find(y))return -1;
    register int ans=0;
    if(depth[x]<depth[y])swap(x,y);
    for(register int i=25;i>=0;i--)//25也可以用log2(K)K=n
        if(depth[fa[x][i]]>=depth[y])ans=max(ans,w[x][i]),x=fa[x][i];//ans和x不能调换顺序,先取最大在更新,是存最大权值
    if(x==y)return ans;
    for(register int i=25;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
        ans=max(ans,max(w[x][i],w[y][i])),x=fa[x][i],y=fa[y][i];
    ans=max(ans,max(w[x][0],w[y][0]));//同上
    return ans;
}//求公共祖先


int main()
{
    int flag=0;
    while(scanf("%d%d",&n,&m)==2)
    {
        if(flag!=0)putchar(10);
        else
        {
            flag=1;
        }//这个是很坑很坑的一个点。坑了一天。最后一组数据不能多空行
    for(register int i=1;i<=m;i++)
    e[i].from=read(),e[i].to=read(),e[i].w=read();
    for(register int i=1;i<=n;i++)
    ba[i]=i;
    kruskal();
    for(register int i=1;i<=n;i++)
    {
        if(!isv[i])
        {
            depth[i]=1;
            dfs(i);
            fa[i][0]=i;
            w[i][0]=-1;//初始化
        }
    }
    for(register int i=1;i<=25;i++)
        for(register int j=1;j<=n;j++)
        {
            fa[j][i]=fa[fa[j][i-1]][i-1];
            w[j][i]=max(w[j][i-1],w[fa[j][i-1]][i-1]);
        }//倍增求最大值
    q=read();
    for(register int i=1;i<=q;i++)
    {
        register int x=read(),y=read();
        int z=LCA(x,y);
        printf("%d",z);
        putchar(10);
    }
    init();多组数据所以再初始化
}
    return 0;
}
```
(~~蒟蒻~~)第一次写题解,多多支持,对了对了,顺便安利一波新博客https://www.cnblogs.com/sscdg/;

猜你喜欢

转载自www.cnblogs.com/sscdg/p/11412592.html