zoukankan      html  css  js  c++  java
  • 长链剖分O(nlogn)-O(1)求K级祖先

    重链剖分是优先选最大的子树的根作为重儿子,而长链剖分是优先选最高的子树作为重儿子,然后把重儿子连成长链。

    一个点到根的路径最多经过 (O(sqrt n)) 条长链。

    可以证明,长链剖分后,树上任意一个点 (u)(k) 级祖先所在的长链长度一定 (geq k)

    我们只需像倍增LCA一样先 (O(nlog n)) 预处理出倍增数组 (anc)。设 (mathrm{highbit}(x)) 表示 (x) 的二进制最高位1的位数,例如 (mathrm{highbit}((1101)_2)=3),那么我们预处理出 (1sim n)(mathrm{highbit})

    然后假设我们当前询问 (u)(k) 级祖先,而 (k=2^{mathrm{highbit}(k)}+k-2^{mathrm{highbit}(k)}),我们先使用 (anc) 数组从 (u) 一步跳到它的 (2^{mathrm{highbit}(k)}) 级祖先 (v),现在问题就转化成了求 (v)(k-2^{mathrm{highbit}(k)}) 级祖先。根据刚刚的结论,树上任意一个点 (u)(k) 级祖先所在的长链长度一定 (geq k),所以现在 (v) 所在的长链的长度一定 (geq 2^{mathrm{highbit}(k)}),而(k-2^mathrm{highbit(k)}<2^mathrm{highbit(k)}),所以我们只需在每条长链的链头建立两个大小都为长链的长度的桶,一个依次存储从链头向下的长链上的结点,一个依次存储从链头向上的结点,然后我们只需从 (v) 这个点由桶直接跳到 (v)(k-2^{mathrm{highbit}(k)}) 级祖先即可。预处理时间复杂度 (O(nlog n)),单次询问时间复杂度 (O(1))

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define RG register int
    #define LL long long
    
    template<typename elemType>
    inline void Read(elemType& T) {
        elemType X = 0, w = 0; char ch = 0;
        while (!isdigit(ch)) { w |= ch == '-';ch = getchar(); }
        while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
        T = (w ? -X : X);
    }
    
    const int maxn = 300010;
    
    struct Graph {
        struct edge { int Next, to; };
        edge G[maxn << 1];
        int head[maxn];
        int cnt;
    
        Graph() :cnt(2) {}
        void clear(int n) {
            cnt = 2;fill(head, head + n + 2, 0);
        }
        void add_edge(int u, int v) {
            G[cnt].to = v;
            G[cnt].Next = head[u];
            head[u] = cnt++;
        }
    };
    Graph G;
    int N, M;
    
    int Deep[maxn], Height[maxn], Anc[maxn][20], Top[maxn], Hson[maxn];
    int highbit[maxn], MaxDeep[maxn];
    vector<int> Up[maxn], Down[maxn], Path;
    
    void DFS1(int u, int fa) {
        Anc[u][0] = fa;
        Height[u] = 1;
        for (int i = 1;i < 20;++i)
            Anc[u][i] = Anc[Anc[u][i - 1]][i - 1];
        for (int i = G.head[u];i;i = G.G[i].Next) {
            int v = G.G[i].to;
            if (v == fa) continue;
            Deep[v] = Deep[u] + 1;
            DFS1(v, u);
            Height[u] = max(Height[u], Height[v] + 1);
            if (Height[Hson[u]] < Height[v]) Hson[u] = v;
        }
        return;
    }
    
    void DFS2(int u, int fa, int top) {
        Path.push_back(u);
        Top[u] = top;
        MaxDeep[top] = max(MaxDeep[top], Deep[u]);
        Down[top].push_back(u);
        if (Hson[u]) DFS2(Hson[u], u, top);
        for (int i = G.head[u];i;i = G.G[i].Next) {
            int v = G.G[i].to;
            if (v == fa || v == Hson[u]) continue;
            DFS2(v, u, v);
        }
        if (u == top) {
            int len = MaxDeep[top] - Deep[u] + 1;
            for (int i = Path.size() - 1;i >= max((int)Path.size() - len - 1, 0);--i)
                Up[top].push_back(Path[i]);
        }
        Path.pop_back();
    }
    
    int KthAnc(int u, int k) {
        if (k > Deep[u]) return 0;
        if (k == 0) return u;
        u = Anc[u][highbit[k]];
        k -= (1 << highbit[k]);
        if (Deep[u] - k == Deep[Top[u]]) return Top[u];
        if (Deep[u] - k > Deep[Top[u]]) return Down[Top[u]][Deep[u] - k - Deep[Top[u]]];
        return Up[Top[u]][Deep[Top[u]] - (Deep[u] - k)];
    }
    
    int main() {
        Read(N);
        for (int i = 1;i <= N - 1;++i) {
            int u, v;
            Read(u);Read(v);
            G.add_edge(u, v);
            G.add_edge(v, u);
        }
        DFS1(1, 0);
        DFS2(1, 0, 1);
        int Max = 1;
        for (int i = 1;i <= N;++i) {
            if ((i >> Max) & 1) ++Max;
            highbit[i] = Max - 1;
        }
        Read(M);
        int lastans = 0;
        while (M--) {
            int u, k;
            Read(u);Read(k);
            u ^= lastans;k ^= lastans;
            lastans = KthAnc(u, k);
            printf("%d
    ", lastans);
        }
    
        return 0;
    }
    
  • 相关阅读:
    微服务简介
    docker跨主机通信扁平化网络的设计与实现
    docker学习ppt
    docker原理介绍
    我的博客搬家啦
    响应式之像素和viewport
    换行+省略号
    ITerms2在mac系统下的安装和配色,并和go2shell关联
    利其器之webstorm快捷键
    React入门 (2)—实现微博展示列表
  • 原文地址:https://www.cnblogs.com/AEMShana/p/14587926.html
Copyright © 2011-2022 走看看