P5903 【模板】树上 k 级祖先

题目传送门:https://www.luogu.com.cn/problem/P5903

树链剖分LCA

主要在于,首先要找x的k级祖先,从这个点出发不断往上找他的顶端端点,如果顶端端点所在的深度小于k级祖先所在的深度(也就是dep[x] - k),继续找他的父亲节点所在链的顶端端点,循环比较顶端端点和k级祖先所在深度,当x的顶端端点的深度大于k级祖先所在的深度时,说明所要找的祖先在这条链上,此时我们可以求得此时x节点的深度与k级祖先深度的差值,最后结果就是x节点的dfs序-差值得到k祖先的dfs序,再转换成原先序列所对应的点。

代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<map>
  6 #include<queue>
  7 #include<set>
  8 #include<cmath>
  9 #include<list>
 10 #include<cstring>
 11 #include<string>
 12 #define ui unsigned int
 13 #define ll long long
 14 #define ull unsigned long long
 15 #define inf 0x3f3f3f3f
 16 #define inff 0x7fffffff
 17 using namespace std;
 18 const int N = 2000000 + 10;
 19 
 20 int dep[N], siz[N], son[N], fa[N], top[N], dfn[N], rnk[N];
 21 int w[N], ctr[N], cnt;
 22 int n, q;
 23 
 24 ui s;
 25 inline ui get(ui x) {
 26     x ^= x << 13;
 27     x ^= x >> 17;
 28     x ^= x << 5;
 29     return s = x;
 30 }
 31 
 32 struct node {
 33     int to, w, next;
 34 }e[N << 2];
 35 int head[N << 2];
 36 int tot;
 37 
 38 void add(int u, int v) {
 39     //e[cnt].w = w;
 40     e[tot].to = v;
 41     e[tot].next = head[u];
 42     head[u] = tot++;
 43 }
 44 
 45 void dfs1(int x) {
 46     siz[x] = 1;
 47     for (int i = head[x]; i + 1; i = e[i].next) {
 48         int to = e[i].to;
 49         if (to == fa[x]) continue;
 50         fa[to] = x;
 51         dep[to] = dep[x] + 1;
 52         dfs1(to);
 53         siz[x] += siz[to];
 54         if (siz[to] > siz[son[x]]) son[x] = to;
 55     }
 56 }
 57 
 58 void dfs2(int x, int tp) {
 59     top[x] = tp;
 60     dfn[x] = ++cnt;
 61     rnk[cnt] = x;
 62     if (son[x]) dfs2(son[x], tp);
 63     for (int i = head[x]; i + 1; i = e[i].next) {
 64         int to = e[i].to;
 65         if (to != son[x] && to != fa[x]) dfs2(to, to);
 66     }
 67     //ctr[x] = cnt;
 68 }
 69 
 70 struct SegTree {
 71     int sum[N << 2], lazy[N << 2];
 72     void PushUp(int rt) {
 73         sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
 74     }
 75     void Build(int l, int r, int rt) {
 76         if (l == r) {
 77             sum[rt] = 0;
 78             return;
 79         }
 80         int mid = (l + r) >> 1;
 81         Build(l, mid, rt << 1);
 82         Build(mid + 1, r, rt << 1 | 1);
 83         PushUp(rt);
 84     }
 85     void PushDown(int rt, int ln, int rn) {
 86         if (lazy[rt]) {
 87             lazy[rt << 1] += lazy[rt];
 88             lazy[rt << 1 | 1] += lazy[rt];
 89             sum[rt << 1] += lazy[rt] * ln;
 90             sum[rt << 1 | 1] += lazy[rt] * rn;
 91             lazy[rt] = 0;
 92         }
 93     }
 94     void Update(int L, int R, int C, int l, int r, int rt) {
 95         if (l >= L && r <= R) {
 96             sum[rt] += (C * (r - l + 1));
 97             lazy[rt] += C;
 98             return;
 99         }
100         int m = (l + r) >> 1;
101         PushDown(rt, m - l + 1, r - m);
102         if (m >= L) Update(L, R, C, l, m, rt << 1);
103         if (m < R) Update(L, R, C, m + 1, r, rt << 1 | 1);
104         PushUp(rt);
105     }
106     int Query(int L, int R, int l, int r, int rt) {
107         if (l >= L && r <= R) {
108             return sum[rt];
109         }
110         int m = (l + r) >> 1;
111         PushDown(rt, m - l + 1, r - m);
112         int ans = 0;
113         if (m >= L) ans = (ans + Query(L, R, l, m, rt << 1));
114         if (m < R) ans = (ans + Query(L, R, m + 1, r, rt << 1 | 1));
115         return ans;
116     }
117 }st;
118 
119 int rt = 1;
120 
121 inline int Jump(int x, int k) {
122     int d = dep[x] - k;
123     if (d <= 0) return 1;
124     //else if (d < 0) return 1;
125     while (dep[top[x]] > d) x = fa[top[x]];
126     d = dep[x] - d;             // 求当前x节点与k级祖先的深度差
127     return rnk[dfn[x] - d];
128 }
129 
130 int jump(int x, int k) {
131     while (k >= dfn[x] - dfn[top[x]] + 1 && x != rt) {
132         k -= (dfn[x] - dfn[top[x]] + 1);
133         x = fa[top[x]];
134     }
135     return rnk[dfn[x] - k];
136 }
137 
138 int main() {
139 
140     ios::sync_with_stdio(false);
141     cin.tie(0), cout.tie(0);
142     cin >> n >> q >> s;
143     memset(head, -1, sizeof(head));
144     tot = 0;
145     for (int i = 1; i <= n; i++) {
146         int x;
147         cin >> x;
148         if (!x) rt = i;
149         else add(x, i);
150     }
151     dep[rt] = 1;
152     dfs1(rt);
153     dfs2(rt, rt);
154     st.Build(1, cnt, 1);
155     int lastans = 0;
156     ll ans = 0;
157     for (int i = 1; i <= q; i++) {
158         int x = (get(s) ^ lastans) % n + 1;
159         int k = (get(s) ^ lastans) % dep[x];
160         lastans = Jump(x, k);
161         ans ^= 1ll * i * lastans;
162     }
163     cout << ans << "\n";
164 
165     return 0;
166 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值