具体思想:
谁抽到这道题,绝壁是祖上没积德倒霉催的;
普通hash存各个根节点过不了,会卡时间;
这道题思想是倍增思想;
也就是说,建立一个二维dp数组,dp[i][j]代表第i个节点的第 2 j 2^j 2j个节点;
转移方程为:
d
p
[
i
]
[
j
]
=
d
p
[
d
p
[
i
]
[
j
−
1
]
[
j
−
1
]
dp[i][j]=dp[dp[i][j-1][j-1]
dp[i][j]=dp[dp[i][j−1][j−1]
难点在于怎么理解;
设想一下,如果j为零,则实际上是她的直接父节点,这是最基础的边界情况;
如果j=1,则要求计算其第 2 1 = 2 2^1=2 21=2个父节点;
按照状态转移方程为 d p [ d p [ i ] [ 0 ] ] [ 0 ] dp[dp[i][0]][0] dp[dp[i][0]][0],实际上就是其父节点的父节点;
如果j=2,则要求计算其第 2 2 = 4 2^2=4 22=4个父节点;
按照状态转移方程为 d p [ d p [ i ] [ 1 ] ] [ 1 ] dp[dp[i][1]][1] dp[dp[i][1]][1],即为第二个父节点的第二个父节点;
根据状态转移方程初始化,必定已经求过;
所以相当于两倍两倍向上搜;
但是还有一个难点,在于我们想要的节点未必是2的倍数,有可能是非2的倍数;
这里采用位运算思想;
对于第三个节点,二进制为11;
此时,诸位取得1,就可以得到是2的几次方;
例如:个位数1,取得其第1个父节点(2的0次方为1);
之后以该节点为起点搜寻第二个父节点(2的1次方为2,第二位1);
此时从最初点开始算就是第三个节点;
具体代码:
class TreeAncestor {
public:
TreeAncestor(int n, vector<int>& parent) {
int len=parent.size();
dp.resize(len);
for(int i=0;i<n;i++){
dp[i].push_back(parent[i]);
}
int j=1;
while(1){
bool flag=true;//判定是不是所有节点的最终父节点都为-1;
for(int i=0;i<n;i++){
int t=dp[i][j-1]!=-1?dp[dp[i][j-1]][j-1]:-1;
dp[i].push_back(t);
if(t!=-1){
flag=false;
}
}
if(flag)
break;
j++;
}
}
int getKthAncestor(int node, int k) {
if(k==0||node==-1)
return node;
int pos=ffs(k)-1;
return pos < dp[node].size() ? getKthAncestor(dp[node][pos], k - (1 << pos)) : -1;
}
private:
vector<vector<int>>dp;
};
/**
* Your TreeAncestor object will be instantiated and called as such:
* TreeAncestor* obj = new TreeAncestor(n, parent);
* int param_1 = obj->getKthAncestor(node,k);
*/