1. 程式人生 > >(未搞懂!!!)BZOJ 4668 冷戰(並查集按秩排序+樸素LCA)

(未搞懂!!!)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 6

Sample Output

0
3

題意大體就是:問你最先能使某兩個點聯通的語句是第幾句

思路:用到按秩排序,用一個數組儲存樹的高度,每次合併都讓矮的那顆樹併到高的樹的下面,以保證最終的樹是高度最低的,在集合中,層數越少,對於每一個節點平均來說,找到根節點所需要查詢的次數就會越小。(注意這裡就不能再用路徑壓縮了,因為壓縮會改變樹的形狀)

在查詢的時候,用到了樸素的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;
}