牛客挑战赛42 C 询问树上节点k层儿子的第k大,分层建立主席树

题目

(https://ac.nowcoder.com/acm/contest/6944/C)

题目描述

小睿睿给了你一颗大小为n的以1为根的树
每次给定x,k,求x的k代兄弟中第k小的权值
k代兄弟指与他拥有相同的第k代祖先的点(包括自己)

输入描述:

第1行2个整数n,m
第2行n个整数val[i],表示各节点的权值
第3至n+1行,每行2个整数i,j,表示节点i,j间有一条边
接下来m行
每行两个整数a,k ( x = ( a xor lastans ) mod n + 1 )
其中lastans为上一个询问的答案,其初始值为0,如上个询问的答案为"?",则为前一个有效答案
输出描述:
共m行,每行1个整数,表示答案,如果该节点没有k个兄弟或第k代祖先,输出"?"(不包含引号)

输入

5 4
1 5 2 5 4
1 2
2 5
3 5
4 5
1 4
4 2
2 2
1 1

输出

?
?
5
4

说明

第一个询问:解码后x=2,没有k=4的祖先,答案为?

第二个询问:解码后x=5,只有1个k=2兄弟(自己),答案为?

第三个询问:解码后x=3,有2个k=2兄弟3,4,权值分别为2,5,第k=2小权值为5,答案为5

第四个询问:解码后x=5,有1个k=1兄弟5,权值为4,答案为4

思路

询问第k级祖先,我们可以用倍增解决。
现在我们考虑解决一个节点的第k层的节点的权值的第k大。

对一个一棵树:我们先按<深度, 和dfs序>排序,得到一个序列rt[]。
树上节点在rt的顺序如图:

一个节点x的第k层组先深度就是d=deep[x]+k。

所以我们只要在第d层找到属于x的子树上的节点就可以,而且一定是连续的。转化成区间第k大。
所以我们按rt[]建立主席树就可以了。

现在我们考虑怎么在第k层节点中找到属于x的子节点的区间。
我们每层节点存入一个vector<>。现在对于第d层,dfs序一定是递增的。

根据dfs序的性质,x的子树dfs序左区间端点L一定>=dfn[x] (x子树的一个点的dfs序),并且右区间端点R<=dfn[x]+siz[x]-1(x子树最后一个点的dfs序)

所以L,R只要二分一下就可以了。时间复杂度O(nlogn)

#include<bits/stdc++.h>
#define LL long long
#define mid (l+r>>1)
using namespace std;

const int N=1e6+10;
struct SegTree {
    int sum[N*40], tot=0;
    int L[N*40], R[N*40];
    void init () {
        for(int i=0; i<=tot; i++) {
            L[i]=R[i]=sum[i]=0;
        }
        tot=0;
    }
    int BT(int l, int r) {
        int rt=++tot;
        sum[rt]=0;
        if(l<r) {
            L[rt]=BT(l, mid);
            R[rt]=BT(mid+1, r);
        }
        return rt;
    }
    int add(int root, int l,int r, int x, int val) {//a[x]+=val
        int rt=++tot;
        L[rt]=L[root], R[rt]=R[root], sum[rt]=sum[root]+val;
        if(l<r) {
            if(x<=mid)
                L[rt]=add(L[root], l, mid, x, val);
            else
                R[rt]=add(R[root], mid+1, r, x, val);
        }
        return rt;
    }
    int query(int x, int y, int l, int r, int k) { //区间[x, y]的第k小

        if(l>=r)
            return l;//得到答案
        int s=sum[L[y]]-sum[L[x]];
        if(s>=k)
            return query(L[x], L[y], l, mid, k);
        else
            return query(R[x], R[y], mid+1, r, k-s);
    }

} Tree;

/************************************/
struct Egde {
    int to, nxt;
} e[N<<1];

int head[N], tot;
int rt[N];//按<深度, dfn>排序的节点
vector<int> g[N];//保存每个深度的点

int deep[N], dfn[N], siz[N], T;
int cmp(const int x, const int y) {
    if(deep[x]==deep[y]) {
        return dfn[x]<dfn[y];
    }
    return deep[x]<deep[y];
}

struct Tree_bfs {

    void add(int x, int y) {
        e[++tot]= {y, head[x]};
        head[x]=tot;
    }
    int fa[N][22], lg[N];
    void init() {
        for(int i = 1; i < N; ++i) {
            lg[i] = lg[i-1] + (1 << lg[i-1] == i);
            g[i].clear();
        }
        memset(head, 0, sizeof(head));
        tot=T=0;
    }
    void dfs(int now, int fath) {
        fa[now][0] = fath;
        dfn[now]=++T;
        deep[now] = deep[fath] + 1; siz[now]=1;
        for(int i = 1; i <= lg[deep[now]]; ++i)
            fa[now][i] = fa[fa[now][i-1]][i-1];
        for(int i = head[now]; i; i = e[i].nxt)
            if(e[i].to != fath){
                dfs(e[i].to, now);
                siz[now]+=siz[e[i].to];
            }
    }
    int LCA(int x, int k) {//x的第k级祖先
        for(int i=20; i>=0; i--) {
            if(k&(1<<i)) {
                x=fa[x][i];
            }
        }
        return x;
    }
    void getrt(int n) {
        for(int i=1; i<=n; i++) {
            rt[i]=i;
        }
        sort(rt+1, rt+n+1, cmp);
        for(int i=1; i<=n; i++) {
            g[deep[rt[i]]].push_back(rt[i]);
        }
    }
} b;
/************************************/

int root[N];
int w[N];
int id[N];//每个点在rt的位置

int main() {

    Tree.init(); b.init();

    int n, m, x, y;
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++) {
        scanf("%d", &w[i]);
    }
    for(int i=1; i<n; i++) {
        scanf("%d%d", &x, &y);
        b.add(x, y);
        b.add(y, x);
    }
    b.dfs(1, 0); b.getrt(n);

    root[0]=Tree.BT(1, n);

    int fa=0, cut=0;
    for(int i=1; i<=n; i++){
        for(auto x: g[i]){
            id[x]=++cut;

            root[x]=Tree.add(root[fa], 1, n, w[x], 1);//按rt数组建立主席树
            fa=x;
        }
    }

    int last=0;
    while(m--){
        int x, y; scanf("%d%d", &x, &y);
        x=x^last; x%=n; x++;

        if(deep[x]<=y){//没有k级祖先
            printf("?\n");
            continue;
        }

        int lca=b.LCA(x, y);
        int d=deep[x];
        int l=0, r=g[d].size()-1, ql=dfn[lca], qr=dfn[lca]+siz[lca]-1, L=0, R=0;
        //L, R保存的是节点
        while(l<=r){
            int md=g[d][l+r>>1];
            if(dfn[md]>=ql){
                L=md;
                r=(l+r>>1)-1;
            }
            else{
                l=(l+r>>1)+1;
            }
        }

        l=0, r=g[d].size()-1, R=0;
        while(l<=r){
            int md=g[d][l+r>>1];
            if(dfn[md]<=qr){
                R=md;
                l=(l+r>>1)+1;
            }
            else{
                r=(l+r>>1)-1;
            }
        }
        if(id[R]-id[L]+1<y){//节点个数<k
            printf("?\n");
            continue;
        }

        last=Tree.query(root[rt[id[L]-1]], root[R], 1, n, y);

        printf("%d\n", last);

    }

    return 0;
}

posted @   liweihang  阅读(206)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
Live2D
欢迎阅读『牛客挑战赛42 C 询问树上节点k层儿子的第k大,分层建立主席树』
点击右上角即可分享
微信分享提示