ST算法是求最近公共祖先的一種 在線 算法,基於RMQ算法,本代碼用雙鏈樹存樹
預處理的時間復雜度是 O(nlog2n) 查詢時間是 O(1) 的
另附上離線算法 Tarjan 的鏈接:
http://www.cnblogs.com/hadilo/p/5840390.html
首先預處理出深度,以及 DFS 序,這里的DFS序是指回溯時也算上,比如
1 void dfs(int x,int dep) 2 { 3 int i; 4 d[x]=dep; 5 a[++top]=x; 6 for (i=down[x];i!=0;i=next[i]) 7 { 8 dfs(i,dep+1); 9 a[++top]=x; 10 } 11 }
然后記錄每個節點在 DFS 序中第一次出現的位置,b[i] 為第 i 號節點第一次出現的位置
1 for (i=1;i<=top;i++) if (b[a[i]]==0) b[a[i]]=i;
開始 DP 處理區間區間內最小值,這里使用 RMQ 算法,其功能類似於線段樹或樹狀數組
f[i][j] 表示從第 i 位開始,連續 2j 個數的最小值,狀態轉移:
1 f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1])
因為它是 2 的冪次方的狀態,所以每次轉移可以看做把當前狀態分為兩個相等的部分,求兩部分的最小值
如: 5 7 3 2 和 4 6 1 5
min=2 min=1
即 f[1][2]=2 f[5][2]=1
所以 f[1][3]=min(f[1][2],f[5][2])=1
初始狀態:f[i][0]=d[a[i]] loc[i][0]=a[i]
注意這里 f 記錄的是它的深度的最小值,而位置用 loc 記錄
1 void init() 2 { 3 int i,j,s,x,k; 4 for (i=1;i<=top;i++) 5 { 6 f[i][0]=d[a[i]]; 7 loc[i][0]=a[i]; 8 } 9 s=log2(top); 10 for (j=1;j<=s;j++) 11 { 12 k=top-(1<<j)+1; 13 for (i=1;i<=k;i++) 14 { 15 x=i+(1<<(j-1)); 16 if (f[i][j-1]<=f[x][j-1]) 17 { 18 f[i][j]=f[i][j-1]; 19 loc[i][j]=loc[i][j-1]; 20 } 21 else 22 { 23 f[i][j]=f[x][j-1]; 24 loc[i][j]=loc[x][j-1]; 25 } 26 } 27 } 28 }
代碼用變量優化了一下常數
接着開始進行詢問
讀入兩個節點,查詢它們第一次出現的位置
在這兩個位置之間的區間查詢最小深度的節點,該節點即為最近公共祖先
查詢區間時,我們把它分成兩個部分,可以有重疊,如
8 9 6 5 6 8 4
這7個節點,把它分成: 8 9 6 5 和 5 6 8 4
min=5 min=4
則最小值為 min(5,4)=4
1 min(f[x][log2(y-x)],f[y-(1<<i)+1][log2(y-x)]);
可以這樣理解:
將兩個位置的距離取個對數記為 i,然后從最左邊,往后共 2i 個數的最小值,這是第一部分
第二個部分是從右邊往左推 2i 個數,即 y-2i+1,然后再往后取 2i 個數
成功將區間分為兩部分
1 scanf("%d",&t); 2 while (t>0) 3 { 4 t--; 5 scanf("%d%d",&x,&y); 6 x=b[x]; 7 y=b[y]; 8 if (x>y) swap(x,y); 9 i=log2(y-x); 10 k=y-(1<<i)+1; 11 printf("%d\n",f[x][i]<f[k][i]?loc[x][i]:loc[k][i]); 12 }
代碼內有常數優化,有的地方思路可能不是很清晰,請諒解
給個完整代碼
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cmath> 5 #include<iostream> 6 #include<algorithm> 7 #define N 100001 8 using namespace std; 9 10 int a[N*2],d[N],down[N],next[N],top,f[2*N][18],loc[2*N][18],n,b[N]; 11 int log2(int x) 12 { 13 int k=0; 14 while (x>1) 15 { 16 x/=2; 17 k++; 18 } 19 return k; 20 } 21 void dfs(int x,int dep) 22 { 23 int i; 24 d[x]=dep; 25 a[++top]=x; 26 for (i=down[x];i!=0;i=next[i]) 27 { 28 dfs(i,dep+1); 29 a[++top]=x; 30 } 31 } 32 void init() 33 { 34 int i,j,s,x,k; 35 for (i=1;i<=top;i++) 36 { 37 f[i][0]=d[a[i]]; 38 loc[i][0]=a[i]; 39 } 40 s=log2(top); 41 for (j=1;j<=s;j++) 42 { 43 k=top-(1<<j)+1; 44 for (i=1;i<=k;i++) 45 { 46 x=i+(1<<(j-1)); 47 if (f[i][j-1]<=f[x][j-1]) 48 { 49 f[i][j]=f[i][j-1]; 50 loc[i][j]=loc[i][j-1]; 51 } 52 else 53 { 54 f[i][j]=f[x][j-1]; 55 loc[i][j]=loc[x][j-1]; 56 } 57 } 58 } 59 } 60 int main() 61 { 62 int i,k,x,y,t; 63 scanf("%d",&n); 64 for (i=1;i<=n;i++) down[i]=d[i]=next[i]=0; 65 for (i=1;i<=n;i++) 66 { 67 scanf("%d",&x); 68 next[i]=down[x]; 69 down[x]=i; 70 } 71 top=0; 72 dfs(down[0],1); 73 for (i=1;i<=top;i++) if (b[a[i]]==0) b[a[i]]=i; 74 init(); 75 scanf("%d",&t); 76 while (t>0) 77 { 78 t--; 79 scanf("%d%d",&x,&y); 80 x=b[x]; 81 y=b[y]; 82 if (x>y) swap(x,y); 83 i=log2(y-x); 84 k=y-(1<<i)+1; 85 printf("%d\n",f[x][i]<f[k][i]?loc[x][i]:loc[k][i]); 86 } 87 return 0; 88 }
版權所有,轉載請聯系作者,違者必究
QQ:740929894