📜  等级祖先问题(1)

📅  最后修改于: 2023-12-03 14:56:42.035000             🧑  作者: Mango

等级祖先问题

介绍

等级祖先问题是指在一棵树上,查询每个节点的第k个祖先节点。在算法竞赛、网络科学、生物学等领域中都有应用。

解决等级祖先问题的算法有多种,本文将介绍两种常见的算法:倍增算法和树状数组算法。

倍增算法

倍增算法是指预处理每个节点的第2^i个祖先,并通过如下代码查询每个节点的第k个祖先:

int getAncestor(int x, int k) {
    int p = x;
    for (int i = 0; k && p; i++, k >>= 1) {
        if (k & 1) p = fa[p][i];
    }
    return p;
}

其中,fa[x][i]表示x的第2^i个祖先。倍增算法需要O(NlogN)的预处理时间,每次查询时间复杂度为O(logN)。

树状数组算法

树状数组算法是指利用树状数组维护每个节点的所有祖先节点,并通过如下代码查询每个节点的第k个祖先:

int lowbit(int x) { return x & -x; }

int n, c[N];

void add(int x, int v) {
    for (int i = x; i <= n; i += lowbit(i)) c[i] += v;
}

int sum(int x) {
    int res = 0;
    for (int i = x; i; i -= lowbit(i)) res += c[i];
    return res;
}

int getKthAncestor(int x, int k) {
    int l = 1, r = n;
    while (l < r) {
        int m = (l + r) >> 1;
        if (sum(m) >= k) r = m;
        else l = m + 1;
    }
    return l == 1 ? 0 : l - 1;
}

其中,c[x]表示节点x的祖先节点个数。树状数组算法需要O(NlogN)的预处理时间,每次查询时间复杂度为O(logN)。

总结

倍增算法和树状数组算法都是常见的解决等级祖先问题的算法。倍增算法适用于查询次数较少的情况,树状数组算法适用于查询次数较多的情况。