【P5903】【模板】樹上 k 級祖先
阿新 • • 發佈:2020-12-30
題目
題目連結:https://www.luogu.com.cn/problem/P5903
給定一棵 \(n\) 個點的有根樹。
有 \(q\) 次詢問,第 \(i\) 次詢問給定 \(x_i, k_i\),要求點 \(x_i\) 的 \(k_i\) 級祖先。
思路
長剖模板題。
長鏈剖分是按照子樹內最長的鏈來樹剖。在求樹上 \(k\) 級祖先時,可以做到 \(O(n\log n)\) 預處理,單次 \(O(1)\) 查詢。
首先我們 dfs 一遍,求出每一個節點的 \(2^k\) 級祖先,並長剖。對於每一條長鏈的頂端節點,假設這條長鏈長度為 \(d\),那麼在這個節點記錄從這個節點開始,往上 \(d\)
詢問時,我們先往 \(x\) 上跳 \(2^{k'}\) 級祖先,滿足 \(2^{k'}\leq k\) 並且儘量大。根據長鏈剖分的性質,這個點所在長鏈長度一定不小於 \(2^{k'}\)。
由於 \(k-2^{k'}\) 一定小於 \(2^{k'}\),所以我們在這條鏈的頂端也一定記錄了 \(x\) 的 \(k\) 級祖先的資訊。所以直接跳到這條長鏈的頂端,根據剩餘步數選擇往下或往上跳若干步即可。
時間複雜度 \(O(n\log n+Q)\)。
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned int uint; const int N=500010,LG=20; int n,Q,rt,last,tot,lg[N],head[N],maxd[N],son[N],dep[N],top[N],f[N][LG+1]; uint seed; ll ans; vector<int> up[N],down[N]; inline uint get(uint x) { x ^= x << 13; x ^= x >> 17; x ^= x << 5; return seed = x; } struct edge { int next,to; }e[N]; void add(int from,int to) { e[++tot]=(edge){head[from],to}; head[from]=tot; } void dfs1(int x) { dep[x]=dep[f[x][0]]+1; for (int i=1;i<=LG;i++) f[x][i]=f[f[x][i-1]][i-1]; for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=f[x][0]) { dfs1(v); maxd[x]=max(maxd[x],maxd[v]+1); if (maxd[v]>maxd[son[x]]) son[x]=v; } } } void dfs2(int x,int tp) { top[x]=tp; if (x==tp) { for (int i=x,j=0;j<=maxd[x];j++,i=f[i][0]) up[x].push_back(i); for (int i=x,j=0;j<=maxd[x];j++,i=son[i]) down[x].push_back(i); } if (son[x]) dfs2(son[x],tp); for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=f[x][0] && v!=son[x]) dfs2(v,v); } } int query(int x,int k) { if (!k) return x; x=f[x][lg[k]]; k-=(1<<lg[k]); k-=dep[x]-dep[top[x]]; x=top[x]; if (k>=0) return up[x][k]; else return down[x][-k]; } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&Q); scanf("%u",&seed); for (int i=1;i<=n;i++) { scanf("%d",&f[i][0]); if (!f[i][0]) rt=i; add(f[i][0],i); } for (int i=2;i<=n;i++) lg[i]=lg[i>>1]+1; dfs1(rt); dfs2(rt,rt); for (int i=1;i<=Q;i++) { int x=(get(seed)^last)%n+1; int k=(get(seed)^last)%dep[x]; last=query(x,k); ans^=1LL*i*last; } printf("%lld",ans); return 0; }