10.3 (2) 到不了
Description wy 和 wjk 是好朋友。 今天他們在一起聊天,突然聊到了以前一起唱過的《到不了》。 “說到到不了,我給你講一個故事吧。” “嗯?” “從前,神和凡人相愛了,憤怒的神王把他們關進了一個迷宮裡,迷宮是由許多棵有根樹組 成的。神王每次把兩個人扔進其中的某一棵有根樹裡面,兩個相鄰節點的距離為 1,兩人的 每一步都只能從兒子走到父親,不能從父親走到兒子,他們約定,走到同一個節點相見,由 於在迷宮裡面行走十分消耗體力,他們決定找出那個使得他們走的總路程最少的節點,他們 當然會算出那個節點了,可是神王有時候會把兩棵有根樹合併為一棵,這下就麻煩了。。。” “唔。。。” [已經瞭解樹,森林的相關概念的同學請跳過下面一段] 樹:由 n 個點,n-1 條邊組成的無向連通圖。 父親/兒子:把樹的邊距離定義為 1,root 是樹的根,對於一棵樹裡面相鄰的兩個點 u,v,到 root 的距離近的那個點是父親,到 root 距離遠的那個點是兒子 森林:由若干棵樹組成的圖 [簡化版題目描述] 維護一個森林,支援連邊操作和查詢兩點 LCA 操作 Input 第一行一個整數 N,M,代表森林裡的節點總數和有根樹的數目。 第二行 M 個整數,第 i 個整數 ri 代表第 i 棵有根樹的根是編號為 ri 的節點 接下來 N-M 行,每行兩個整數 u,v 表示 u 和 v 相鄰 接下來一行一個整數 Q,表示 Q 個事件發生了
接下來 Q 行,每行若干個整數,表示一個事件 如果第一個數 op=1,接下來兩個整數 u,v,代表神王把 u 號節點所在的樹和 v 號節點所在 的樹合併到一起(即 u 到 v 連了一條邊),新的根為原來 u 號節點所在的樹的根(如果 u,v 已經聯通,忽略這個事件)。 如果第一個數 op=2,接下來兩個整數 u,v,代表一次詢問,當一個人在 u 號節點,一個 人在 v 號節點,詢問他們找到的那個節點的編號 Output 對於每一個詢問(op=2 的操作),輸出一行一個整數,代表節點編號,如果 u,v 不聯通, 輸 出 orzorz。 Sample Input 【樣例 1】 2 2 1 2 2 1 1 2 2 1 2 【樣例 2】 2 2 1 2 2 1 2 1 2 1 2 Sample Output 【樣例 1】 1 【樣例 2】 2 Hint 【資料範圍】 對於 30%的資料 1 ≤ N ≤ 1000 1 ≤ Q ≤ 1000 對於 100%的資料 1 ≤ N ≤ 100000 1 ≤ Q ≤ 100000
題解:啟發式合併兩棵樹的 LCA 倍增陣列,每次總是把節點數少的那棵樹合併到節點多的那棵樹上
重新建立連邊的那一可樹,找距離時即從深度比較深的lca入手
#include<bits/stdc++.h> using namespace std; #define ll long long #define out(x) print(x) const int MAXN = 1e5 + 5; int f[MAXN][21]; int dep[MAXN],rt[MAXN],root[MAXN]; int size[MAXN]; int head[MAXN*2],cnt = 0;
struct node { int next,to; } e[MAXN*2];
void add(int u,int v) { e[++cnt].next = head[u],e[cnt].to = v,head[u] = cnt; e[++cnt].next = head[v],e[cnt].to = u,head[v] = cnt; } int n,m,q,E;
void rebuild(int x,int fr) { for(int i = 1; i <= 20; i++) f[x][i] = f[f[x][i-1]][i-1]; for(int i = head[x]; i; i = e[i].next) { int to = e[i].to; if(to == fr) continue; dep[to] = dep[x] + 1; f[to][0] = x; rebuild(to,x); } }
void dfs(int x,int fr) { f[x][0] = fr; for(int i = head[x]; i; i = e[i].next) { int to = e[i].to; if(to == fr) continue; dep[to] = dep[x] + 1; dfs(to,x); size[x] += size[to]; } }
int get(int x) { for(int i = 20; i >= 0; i--) if(f[x][i]) x = f[x][i]; return x; }
int lca(int x,int y) { if(dep[x] < dep[y]) swap(x,y); for(int i = 20; i >= 0; i--) if(dep[x] - (1 << i) >= dep[y]) x = f[x][i]; if(x == y) return x; for(int i = 20; i >= 0; i--) if(f[x][i] != f[y][i]) x = f[x][i],y = f[y][i]; return f[x][0]; }
int main() { cin>>n>>m; for(int i = 1; i <= m; i++) cin>>rt[i]; int e; cin>>e; for(int i = 1; i <= n-m; i++) { int x,y; cin>>x>>y; add(x,y); } for(int i = 1; i <= n; i++) size[i] = 1; for(int i = 1; i <= m; i++) { dep[rt[i]] = 1; dfs(rt[i],0); } for(int j = 1; j <= 20; j++) for(int i = 1; i <= n; i++) f[i][j] = f[f[i][j-1]][j-1]; for(int i = 1; i <= n; i++) { int o = get(i); if(o) root[i] = o; else root[i] = i; } cin>>q; for(int i = 1; i <= q; i++) { int o,u,v; cin>>o>>u>>v; if(o == 1) { int x = get(u),y = get(v); if(x == y) continue; if(size[x] > size[y]) { f[v][0] = u; size[x] += size[y]; dep[v] = dep[u] + 1; root[y] = root[x]; add(u,v); rebuild(v,u); } else { f[u][0] = v; size[y] += size[x]; dep[u] = dep[v] + 1; root[y] = root[x]; add(u,v); rebuild(u,v); } } else { int a = u; int b = v; if(get(a) != get(b)) { cout << "orzorz" << endl; continue; } else { int c = get(a); int a = lca(u,v); int b = lca(root[c],v); if(dep[a] < dep[b]) a = b; b = lca(root[c],u); if(dep[a] < dep[b]) a = b; cout<<a<<endl; } } } return 0; }