【NOIP2018模擬A組9.25】到不了
Description
wy 和 wjk 是好朋友。
今天他們在一起聊天,突然聊到了以前一起唱過的《到不了》。
“說到到不了,我給你講一個故事吧。”
“嗯?”
“從前,神和凡人相愛了,憤怒的神王把他們關進了一個迷宮裡,迷宮是由許多棵有根樹組 成的。神王每次把兩個人扔進其中的某一棵有根樹裡面,兩個相鄰節點的距離為 1,兩人的 每一步都只能從兒子走到父親,不能從父親走到兒子,他們約定,走到同一個節點相見,由 於在迷宮裡面行走十分消耗體力,他們決定找出那個使得他們走的總路程最少的節點,他們 當然會算出那個節點了,可是神王有時候會把兩棵有根樹合併為一棵,這下就麻煩了。。。”
“唔。。。”
[已經瞭解樹,森林的相關概念的同學請跳過下面一段]
樹:由 n 個點,n-1 條邊組成的無向連通圖。
父親/兒子:把樹的邊距離定義為 1,root 是樹的根,對於一棵樹裡面相鄰的兩個點 u,v,到 root 的距離近的那個點是父親,到 root 距離遠的那個點是兒子
森林:由若干棵樹組成的圖
[簡化版題目描述]
維護一個森林,支援連邊操作和查詢兩點 LCA 操作
Data Constraint
對於 30%的資料 1 ≤ N ≤ 1000 1 ≤ Q ≤ 1000
對於 100%的資料 1 ≤ N ≤ 100000 1 ≤ Q ≤ 10000
Solution
很容易想到LCT,但是LCT怎麼求頂點為根時的LCA呢?
有一個很簡單易懂的方法:
對於兩個點u,v,我們要求以當前根為根的LCA,那麼我們需要在access操作中返回一個值——最後一個操作的splay的根。這樣我們發現,access(v),再access(u)時會返回u,v的LCA。為什麼呢?因為在LCA以上一直到根的路徑中,兩個點到根的路徑都包含,所以噹噹前點旋為根,然後再跳虛邊沒有了的時候,這個點就一定是u,v的LCA。
比較抽象,具體見程式碼。
#include<cstdio>
#include<iostream>
#include<cstring>
#define maxn 100010
#define maxm 100010
#define maxq 100010
using namespace std;
int n,m,q,tot;
int root[maxm],d[maxn];
struct Moon{
int pf,fa,son[2],tag;
}t[maxn];
int fa[maxn];
void make(int x){
swap(t[x].son[0],t[x].son[1]);
t[x].tag^ =1;
}
void clear(int x){
if(t[x].tag){
make(t[x].son[0]),make(t[x].son[1]);
t[x].tag=0;
}
}
void remove(int x,int y){
for (d[0]=0;x!=y;x=t[x].fa) d[++d[0]]=x;
for (int i=d[0];i>=1;--i) clear(d[i]);
}
int get(int x){
return t[t[x].fa].son[1]==x;
}
void rotate(int x){
int y=t[x].fa,k=get(x);
if(t[y].fa) t[t[y].fa].son[get(y)]=x;
if(t[x].son[!k]) t[t[x].son[!k]].fa=y;
t[y].son[k]=t[x].son[!k],t[x].fa=t[y].fa,t[y].fa=x,t[x].son[!k]=y;
t[x].pf=t[y].pf;
}
void splay(int x,int y){
remove(x,y);
while(t[x].fa!=y){
if(t[t[x].fa].fa!=y)
if(get(x)==get(t[x].fa)) rotate(t[x].fa);
else rotate(x);
rotate(x);
}
}
int access(int x){
int z;
for (int y=0;x;y=x,x=t[x].pf){
splay(x,0);
t[t[x].son[1]].fa=0,t[t[x].son[1]].pf=x;
t[x].son[1]=y,t[y].fa=x,t[y].pf=0,z=x;//z用來儲存最後一次的根
}
return z;
}
int Get(int x){
if(fa[x]==x) return x;
return fa[x]=Get(fa[x]);
}
void makeroot(int x){
access(x);
splay(x,0);
make(x);
}
void link(int x,int y){
makeroot(y);
int xx=Get(x),yy=Get(y);
if(xx!=yy) fa[xx]=yy,t[y].pf=x;
}
int findans(int x,int y){
if(Get(x)!=Get(y)) return -1;
int q=access(y);
int p=access(x);
return p;
}
int main(){
freopen("arrival.in","r",stdin);
freopen("arrival.out","w",stdout);
scanf("%d %d",&n,&m);
int i,j,x,y,kind,ans;
for (i=1;i<=m;++i) scanf("%d",&root[i]);
for (i=1;i<=n;++i) fa[i]=i;
for (i=1;i<=n-m;++i){
scanf("%d %d",&x,&y);
link(x,y);
}
for (i=1;i<=m;++i) makeroot(root[i]);
scanf("%d",&q);
while(q--){
scanf("%d%d%d",&kind,&x,&y);
if(kind==1){
if(Get(x)!=Get(y))link(x,y);
}
else{
ans=findans(x,y);
if(ans==-1) printf("orzorz\n");
else printf("%d\n",ans);
}
}
}