天天寫演算法之離線Tarjan演算法Closest Common Ancestors
阿新 • • 發佈:2019-01-24
這些延伸的題目都是我在做hdu的時候由於原題比較難,所以才會找更加基礎的來做。對於ACM小白來說,基本上是每隔4道題就必然有一個新的題型。今天就是離線Tarjan演算法。emmmLCA我還沒完全寫對。我要對這個教程裡沒有闡述的東西進行一下深剖。比如 5 4 3 2 1 6這個地方是4 3 2 是5的兒子,1 6 是4的兒子這裡用到了並查集,首先我們起初的時候要把所有的pre【i】=i,就是所有節點的祖先節點都是本身。這一點很重要,不單單是並查集的應用那麼簡單。
首先再賦值的時候也就是ask的時候我們要ask[i][u]和ask[u][i]都要標記上,因為假如你第一遍走到ask[i][u]發現i沒有訪問過,那麼問題就來了,這個ask就再也不會被訪問了,所以需要另一個等到訪問到i的時候就會發現u被訪問了,就能輸出結果了。另一個點,就是在執行 dfs(son)之後,才會把這些孩子的父親設定為離他最近的父親,要知道在這之前他們所有節點的父親都是他自己。 比如問我1 6 的祖先,此時已經找到了6,這個時候1已經訪問過了,訪問過了代表什麼呢,代表他的father被設定成了4,此時還沒訪問完4的所有兒子,說明還沒有dfs(4)還沒有完成,那麼father(4)也就還是等於4.所以能輸出正確結果,他是依靠這個特性保持最近父節點的。加入我們再找1 3 的dfs(4)已經回來了,說明了什麼?說明father(4)已經被設定成了5,此時find(4)就等於了5.程式碼來一波:
上程式碼:
void dfs(int u)
{
for(int i = 1; i <= n; i++)
{
if(visit[i]&&ask[u][i])
{
LCA[u][i] = Find(i);
}
}
visit[u] = true;
for(int i = 0; i < g[u].size(); i++)
{
int son = g[u][i];
dfs(son);
father[son] = u;
}
}
visit表示是否被訪問了。ask表示詢問的節點結果,比如我問你1和6的最近祖先是什麼。g是一個vector的陣列,儲存的是g[i]儲存的是i這個節點所有的兒子。首先再賦值的時候也就是ask的時候我們要ask[i][u]和ask[u][i]都要標記上,因為假如你第一遍走到ask[i][u]發現i沒有訪問過,那麼問題就來了,這個ask就再也不會被訪問了,所以需要另一個等到訪問到i的時候就會發現u被訪問了,就能輸出結果了。另一個點,就是在執行 dfs(son)之後,才會把這些孩子的父親設定為離他最近的父親,要知道在這之前他們所有節點的父親都是他自己。
#include<stdio.h> #include<iostream> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int maxn = 1000; int ask[maxn][maxn];//儲存詢問 int ans[maxn];//儲存祖先i出現過的次數 int n,m; vector<int> g[maxn];//儲存兒子 int root;//樹的根 bool visit[maxn]; bool isroot[maxn]; int father[maxn]; int Find(int x) { if(father[x] == x) return x; else return father[x] = Find(father[x]); } void init() { memset(ans,0,sizeof(ans)); memset(visit,false,sizeof(visit)); memset(isroot,true,sizeof(isroot)); memset(ask,0,sizeof(ask)); for(int i = 1; i <= n; i++) { g[i].clear(); father[i] = i; } } void LCA(int root) { for(int i = 1; i <= n; i++) { if(visit[i]&&ask[root][i]) { ans[Find(i)] += ask[root][i]; } } visit[root] = true; for(int i = 0; i < g[root].size(); i++) { int term = g[root][i]; LCA(term); father[term] = root; } } int main() { while(~scanf("%d",&n)) { init(); int f,s,num; for(int i = 1; i <= n; i++) { scanf("%d:(%d)",&f,&num); for(int j = 1; j <= num; j++) { scanf(" %d",&s); isroot[s] = false; g[f].push_back(s); } } for(int i = 1; i <= n; i++) { if(isroot[i]) { root = i; break; } } scanf("%d",&m); int u,v; for(int i = 1; i <= m; i++) { scanf(" (%d %d)",&u,&v); ask[u][v]++; ask[v][u]++; } LCA(root); for(int i = 1; i <= n; i++) { if(ans[i]) { printf("%d:%d\n",i,ans[i]); } } } return 0; }