1. 程式人生 > 實用技巧 >牛客挑戰賽42 C 詢問樹上節點k層兒子的第k大,分層建立主席樹

牛客挑戰賽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一定>=dfnx,並且右區間端點R<=dfn[x]+siz[x]-1(x子樹最後一個點的dfs序)

所以L,R只要二分一下就可以了。

#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;
}