並查集(按秩合併)
阿新 • • 發佈:2019-02-05
並查集-按秩合併
題目:UVA-11354
題目大意:給出一張n個點m條邊的無向圖, 每條邊有一個危險度,有q個詢問, 每次給出兩個點s、t,找一條路, 使得路徑上的最大危險度最小。
思路:首先,我們可以發現,如果求一個最小生成樹, 那麼任意兩點, 在生成樹上有唯一路徑, 而且這條路徑上的最大危險值一定最小。 但是n和q都太大, 如果直接順著樹走,每次詢問最大複雜度O(n), 那麼複雜度高達O(n^2),會超時。 我們知道, 並查集在用了路徑壓縮之後效率高達O(n), 但是卻破壞了樹形結構, 所以不能用路徑壓縮。 然而我們經常忽視了按秩合併這個方法, 即使不用路徑壓縮, 僅僅靠按秩合併, 複雜度也可低至O(logn)。 因此我們只需按秩合併, 然後詢問的時候向根回溯就行了, 複雜度mlogn。
注意:秩的意思就是樹的高度,按秩合併過後並查集的結構為樹形結構,
樣例:
4 5
1 2 10
1 3 20
1 4 100
2 4 30
3 4 10
2
1 4
4 1
此樣例按秩合併過後的結構如下:
可以看出來按秩合併過後,此並查集呈現出樹形的結構,並且呈遞增,所此每次訪問的肯定是從小到大的線路,因此路徑上的最大危險度肯定是最小的。。。
圖手畫的,不是很好看,還請見諒。。。。。。。
程式碼如下:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
int n,m;
int pre[50000+10],ra[50000+10],vis[50000+10],e[50000+10];
struct node
{
int u,v,w;
}p[100000+10];
bool cmp(node x,node y)
{
return x.w<y.w;
}
int Find(int x)
{
return pre[x]==x?x:Find(pre[x]);
}
void Union(int a,int b,int c)
{
int x=Find(a);
int y=Find(b);
if(x==y)
return ;
if(ra[x]<ra[y])
{
pre[x]=y;
e[x]=c;//e陣列記錄路徑上的大小
}
else
{
pre[y]=x;
e[y]=c;
if(ra[x]==ra[y])
ra[x]++;//ra陣列就是記錄樹的高度,也就是所謂的秩
}
}
int qurey(int x,int y)//查詢
{
int ans1=0,ans2=-1;
int cnt=x;//先從x往y查詢,
while(true)
{
vis[cnt]=ans1;
if(cnt==pre[cnt])
break;
ans1=max(ans1,e[cnt]);
cnt=pre[cnt];
}
cnt=y;
while(true)
{
if(vis[cnt]>=0)//如果剛剛x在y下面,那麼就能掃到y,這裡判斷一下是的話就可以直接返回了,
{
ans2=max(ans2,vis[cnt]);
break;
}
if(cnt==pre[cnt])
break;
ans2=max(ans2,e[cnt]);
cnt=pre[cnt];
}
cnt=x;
while(true)//如果x是在y上面,那剛剛x以上的樹節點的vis值就被改變了,需要復原,以便下一次的查詢,
{
vis[cnt]=-1;
if(cnt==pre[cnt])
break;
cnt=pre[cnt];
}
return ans2;
}
void init()
{
sort(p,p+m,cmp);
for(int i=1;i<=n;i++)
{
pre[i]=i;
ra[i]=0;
vis[i]=-1;
}
for(int i=0;i<m;i++)
{
Union(p[i].u,p[i].v,p[i].w);
}
//cout<<"pre[4]="<<pre[4]<<" "<<"pre[3]="<<pre[3]<<endl;
}
int main()
{
int kase=0;
while(cin>>n>>m)
{
if(kase)
cout<<endl;
else
kase++;
for(int i=0;i<m;i++)
cin>>p[i].u>>p[i].v>>p[i].w;
init();
int q;
cin>>q;
for(int i=0;i<q;i++)
{
int u,v;
cin>>u>>v;
cout<<qurey(u,v)<<endl;
}
}
return 0;
}