1. 程式人生 > >BZOJ 3732 Network Kruskal重構樹

BZOJ 3732 Network Kruskal重構樹

題目大意:給定一個n個點m條邊的無向連通圖,k次詢問兩點之間所有路徑中最長邊的最小值

Kruskal+倍增LCA做法見http://blog.csdn.net/popoqqq/article/details/39755703

LCT做法見http://blog.csdn.net/popoqqq/article/details/39929277

Kruskal重構樹真是強大……一不小心手滑就RANK1啥的……

每加入一條邊時,我們並不連結這條邊的兩端點,而是把這條邊兩端點所在並查集的根連線起來,而且是按秩合併

這樣做的好處就是任何一個節點到根的路徑長度不超過logn

且由於Kruskal重構樹的性質,兩點之間的最長邊和原圖相同,且最長邊的一端連線的一定是兩點的LCA

於是直接暴力向上找就行 如果加上倍增的話或許能優化到O(loglogn)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 15100
using namespace std;
struct abcd{
    int x,y,f;
    bool operator < (const abcd &Y) const
    {
        return f < Y.f ;
    }
}edges[M<<1];
int n,m,k;
int belong[M],fa[M],size[M],dis[M],dpt[M];
int Find(int x)
{
    if(!belong[x])
        belong[x]=x,size[x]=1;
    if(belong[x]==x)
        return x;
    return belong[x]=Find(belong[x]);
}
int Get_Depth(int x)
{
    if(dpt[x]) return dpt[x];
    if(!fa[x]) return dpt[x]=1;
    return dpt[x]=Get_Depth(fa[x])+1;
}
void Kruskal()
{
    int i;
    sort(edges+1,edges+m+1);
    for(i=1;i<=m;i++)
    {
        int x=Find(edges[i].x);
        int y=Find(edges[i].y);
        if(x==y) continue ;
        if(size[x]>size[y])
            swap(x,y);
        belong[x]=y;
        size[y]=max(size[y],size[x]+1);
        fa[x]=y;
        dis[x]=edges[i].f;
    }
}
int Query(int x,int y)
{
    int re=0;
    if(dpt[x]<dpt[y])
        swap(x,y);
    while(dpt[x]>dpt[y])
        re=max(re,dis[x]),x=fa[x];
    while(x!=y)
        re=max(re,dis[x]),re=max(re,dis[y]),x=fa[x],y=fa[y];
    return re;
}
int main()
{
    int i,x,y;
    cin>>n>>m>>k;
    for(i=1;i<=m;i++)
        scanf("%d%d%d",&edges[i].x,&edges[i].y,&edges[i].f);
    Kruskal();
    for(i=1;i<=n;i++)
        Get_Depth(i);
    for(i=1;i<=k;i++)
    {
        scanf("%d%d",&x,&y);
        printf("%d\n", Query(x,y) );
    }
}