(未搞懂!!!)BZOJ 4668 冷戰(並查集按秩排序+樸素LCA)
Description
1946 年 3 月 5 日,英國前首相溫斯頓·丘吉爾在美國富爾頓發表“鐵
幕演說”,正式拉開了冷戰序幕。
美國和蘇聯同為世界上的“超級大國”,為了爭奪世界霸權,兩國及其
盟國展開了數十年的鬥爭。在這段時期,雖然分歧和衝突嚴重,但雙方都
盡力避免世界範圍的大規模戰爭(第三次世界大戰)爆發,其對抗通常通
過區域性代理戰爭、科技和軍備競賽、太空競爭、外交競爭等“冷”方式進
行,即“相互遏制,不動武力”,因此稱之為“冷戰”。
Reddington 是美國的海軍上將。由於戰爭局勢十分緊張,因此他需要
時刻關注著蘇聯的各個活動,避免使自己的國家陷入困境。蘇聯在全球擁
有 N 個軍工廠,但由於規劃不當,一開始這些軍工廠之間是不存在鐵路
的,為了使武器製造更快,蘇聯決定修建若干條道路使得某些軍工廠聯通。
Reddington 得到了蘇聯的修建日程表,並且他需要時刻關注著某兩個軍工
廠是否聯通,以及最早在修建哪條道路時會聯通。具體而言,現在總共有
M 個操作,操作分為兩類:
• 0 u v,這次操作蘇聯會修建一條連線 u 號軍工廠及 v 號軍工廠的鐵
路,注意鐵路都是雙向的;
• 1 u v, Reddington 需要知道 u 號軍工廠及 v 號軍工廠最早在加入第
幾條條鐵路後會聯通,假如到這次操作都沒有聯通,則輸出 0;
作為美國最強科學家, Reddington 需要你幫忙設計一個程式,能滿足
他的要求。
Input
第一行兩個整數 N, M。
接下來 M 行,每行為 0 u v 或 1 u v 的形式。
資料是經過加密的,對於每次加邊或詢問,真正的 u, v 都等於讀入的
u, v 異或上上一次詢問的答案。一開始這個值為 0。
1 ≤ N, M ≤ 500000,解密後的 u, v 滿足1 ≤ u, v ≤ N, u不等於v
Output
對於每次 1 操作,輸出 u, v 最早在加入哪條邊後會聯通,若到這個操
作時還沒聯通,則輸出 0。
Sample Input
5 9
0 1 4
1 2 5
0 2 4
0 3 4
1 3 1
0 7 0
0 6 1
0 1 6
1 2 6Sample Output
0
3
5
題意大體就是:問你最先能使某兩個點聯通的語句是第幾句
思路:用到按秩排序,用一個數組儲存樹的高度,每次合併都讓矮的那顆樹併到高的樹的下面,以保證最終的樹是高度最低的,在集合中,層數越少,對於每一個節點平均來說,找到根節點所需要查詢的次數就會越小。(注意這裡就不能再用路徑壓縮了,因為壓縮會改變樹的形狀)
在查詢的時候,用到了樸素的LCA,
把深的節點先往上跳。深度相同了之後,一起往上跳。最後跳到一起了就是LCA了。
這裡我還沒有徹底的明白為什麼要用LCA???希望有大神看到可以幫我講解一下,萬分感激!
AC程式碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 500010
using namespace std;
int f[MAX],v[MAX],h[MAX],dep[MAX];
int la,n,m;
void init() //初始化
{
for(int i=1;i<=n;i++)
f[i]=i;
}
int getf(int x) //找祖先,這裡不能用路徑壓縮優化,否則破壞樹的形狀
{
return f[x]==x?x:getf(f[x]);
}
void pre(int x)//求深度
{
if(f[x]==x) return;
pre(f[x]);
dep[x]=dep[f[x]]+1;
}
int ask(int x,int y) //樸素的LCA
{
pre(x);pre(y);
if(dep[x]<dep[y]) swap(x,y);
int re=0;
while(dep[x]>dep[y]&&x!=y)
{
re=max(re,v[x]);
x=f[x];
}
while(x!=y)
{
re=max(re,max(v[x],v[y]));
x=f[x];
y=f[y];
}
return re;
}
int main()
{
int op,x,y;
int cnt=0;
scanf("%d%d",&n,&m);
init();
while(m--)
{
scanf("%d%d%d",&op,&x,&y);
x^=la;//題目中要求的異或
y^=la;
if(op==0)
{
int fx=getf(x),fy=getf(y);
cnt++;
if(fx!=fy) //按秩排序,保證樹的高度儘可能的低
{
if(h[fx]<=h[fy])
{
f[fx]=fy;
v[fx]=cnt;
if(h[fx]==h[fy])
h[fy]++;
}
else
{
f[fy]=fx;
v[fy]=cnt;
}
}
}
else //查詢
{
int fx=getf(x),fy=getf(y);
if(fx==fy) la=ask(x,y);
else la=0;
cout<<la<<endl;
}
}
return 0;
}