• 洛谷P5903 树上k级祖先 长链剖分


    长链剖分:

    与重链剖分类似,重链剖分的关键字是子树规模,长链剖分的关键字是子树深度。

    长链剖分有一些重要性质:

    性质一: x x x k k k级祖先所在的长链长度必然 ≥ k \geq k k,证明如下:

    x x x k k k级祖先为 y y y

    x x x y y y在同一条长链上,既然 d i s ( x , y ) = = k dis(x,y)==k dis(x,y)==k,那么长链长度一定 ≥ k \geq k k

    若他们不在一条长链上,如图, t t t y y y所在的长链的末端
    在这里插入图片描述

    因为 d i s ( x , y ) = = k dis(x,y)==k dis(x,y)==k,即 o y + o x = = k oy+ox==k oy+ox==k,如果 o x > o t ox>ot ox>ot,那么 x x x在的链就会是 y y y的重链了,因为 o o o总是选择深度深的儿子作为深儿子,因此,一定有 o x ≤ o t ox\leq ot oxot存在,即 o y + o x ≤ o y + o t oy+ox\leq oy+ot oy+oxoy+ot,即 k ≤ o y + o t ≤ l e n [ y ] k\leq oy+ot \leq len[y] koy+otlen[y]

    性质二: x x x沿长链向上跳至根的跳跃次数最大是 O ( n ) O(\sqrt{n}) O(n 级别的,证明如下:

    因为每次跳跃 k k k长度后链的长度至少是 ≥ k \geq k k的,因为每次都是跳至顶,链的长度一定递增(可能取等),但链顶点跳至其他链需要多 1 1 1的跳跃,所以每次的跳跃长度严格递增,考虑跳跃次数最差的情况,跳跃长度依次为 1 , 2 , 3.... 1,2,3.... 1,2,3....,求和 t ( t + 1 ) 2 ≤ n \frac{t(t+1)}{2}\leq n 2t(t+1)n

    解这个方程就有最大跳跃次数了。

    树上 k k k级祖先的应用:

    要求 x x x k k k级祖先,先找到满足 2 h ≤ k ≤ 2 h + 1 2^h\leq k \leq 2^{h+1} 2hk2h+1 h h h,然后跳跃至 x x x 2 h 2^h 2h级祖先 y y y,根据性质 1 1 1,此时 l e n [ y ] ≥ 2 h len[y]\geq 2^h len[y]2h,剩余需要跳的步数为 k − 2 h ≤ 2 h k-2^h\leq 2^h k2h2h(因为 h h h满足 k ≤ 2 h + 1 k\leq 2^{h+1} k2h+1),得到 k − 2 h ≤ l e n [ y ] k-2^h\leq len[y] k2hlen[y],又因为 k − 2 h ≥ 0 k-2^h\geq0 k2h0,所以至少是 y y y往上跳,最差情况下最高跳至 t o p [ y ] top[y] top[y] l e n [ y ] len[y] len[y]级祖先,那么只需要维护每根链顶点 p p p [ 1 , l e n [ p ] ] [1,len[p]] [1,len[p]]祖先和儿子,就可以 O ( 1 ) O(1) O(1)查询了

    #include
    #define ll long long
    #define ui unsigned int
    using namespace std;
    
    ui s;
    inline ui get(ui x) 
    {
        x ^= x << 13;
        x ^= x >> 17;
        x ^= x << 5;
        return s = x; 
    }
    
    struct way
    {
        int to,next;
    }edge[1000005];
    int cnt,head[500005];
    
    void add(int u,int v)
    {
        edge[++cnt].to=v;
        edge[cnt].next=head[u];
        head[u]=cnt;
    }
    
    int n,q,root,depth[500005],top[500005],len[500005];
    int max1[500005],son[500005],f[500005][21];
    vector<vector<int>>upp(500005),downn(500005);
    
    void dfs1(int u)
    {
        depth[u]=depth[f[u][0]]+1; max1[u]=1; 
        for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1];
        for(int i=head[u];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==f[u][0]) continue;
            dfs1(v);
            if(max1[v]>max1[son[u]]) son[u]=v;
        }
        max1[u]+=max1[son[u]];
    }
    
    void dfs2(int u,int topf)
    {
        top[u]=topf; len[u]=depth[u]+max1[u]-1-depth[top[u]]+1;
        if(son[u]) dfs2(son[u],topf);
        for(int i=head[u];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==f[u][0]||v==son[u]) continue;
            dfs2(v,v);
        }
    }
    
    int query(int x,int k)
    {
        if(!k) return x;
        int base=(int)(log(k)/log(2));
        x=f[x][base]; k-=1<<base;
        k-=depth[x]-depth[top[x]]; x=top[x];
        if(!k) return x;
        else if(k>0) return upp[x][k-1];
        return downn[x][-k-1];
    }
    
    int main()
    {
        cin>>n>>q>>s; ll tot=0,ans=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&f[i][0]);
            if(!f[i][0]) root=i;
            else
            {
                add(i,f[i][0]); 
                add(f[i][0],i);
            }
        }
        dfs1(root); dfs2(root,root);
        for(int i=1;i<=n;i++)
        {
            // printf("max1[%d]=%d\n",i,len[i]);
            if(i!=top[i]) continue;
            for(int j=1,x=f[i][0];j<=len[i];j++,x=f[x][0]) upp[i].push_back(x);
            for(int j=1,x=son[i];j<=len[i];j++,x=son[x]) downn[i].push_back(x);
        }
        for(int i=1;i<=q;i++)
        {
            int x=(get(s)^ans)%n+1,k=(get(s)^ans)%depth[x];
            ans=query(x,k); tot^=i*ans;
        }
        cout<<tot;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
  • 相关阅读:
    怎么给视频去水印?手把手教你去水印
    【毕业季_进击的技术er】大四在兵荒马乱中如期而至,迷茫在跌跌撞撞中戛然而止。而我的梦想从这里开始!
    Redis之list类型
    Flutter 错误must be a valid Dart package name
    Pico Unity Integration SDK开发笔记1
    java每日一记 —— List创建的方式判断
    gradle-4-构建有向无环图
    2019版本idea启动tomcat8.5版本控制台中文乱码
    Spring Boot与Kubernetes:现代云部署的完美组合
    java 串口通讯
  • 原文地址:https://blog.csdn.net/stdforces/article/details/126891589