题目链接: P5903 树上 k 级祖先
大致题意
不 要 问 我 题 目 为 什 么 带 空 格.
解题思路
树上倍增 不会吧, 不会吧, 你还没学LCA吗? (如果你学过了LCA, 那就比较好理解这个题.)
首先对于一个节点, 如果我们想求他的第k级祖先, 我们不妨把k看成二进制的形式.
假设 k = 37 = (100101)2, 那么对于节点x, 其37级祖先我们可以这样去求:
x -> x的第32级祖先(称为: x32) -> x32的第4级祖先(称为: x32,4) -> x32,4的第1级祖先(称为: x32,4,1).
最后我们得到的x32,4,1节点 即为x的第37级祖先.
我们发现实际上这个过程就是在用2的整次方凑出k. 我们需要处理的数据也很明显, 即: 对于每个节点, 其2的整次幂级祖先是谁. 我们用f[N][log2(N)]
来存储预处理出的信息.
对于一个节点x而言, 我们如果从根节点开始, 进行dfs, 那么我们一定先遍历过其父亲节点fa, 然后再到x.
即: x节点的每一个祖先节点的信息我们都已经与处理完了(因此我们可以直接利用)
当我们想算x节点的第2i级祖先时, 我们可以由 x节点的第2i-1级祖先的第2i-1级祖先来得到.
即: f[x][i] = f[f[x][i - 1]][i - 1]
预处理复杂度: O(nlogn) 查询复杂度: 最坏O(logn), 与k2中1的个数相关.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E5 + 10;
#define ui unsigned int
ui s;
inline ui get(ui x) {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return s = x;
}
vector<int> edge[N];
int depth[N], f[N][21];
void dfs(int x, int fa) {
depth[x] = depth[fa] + 1;
f[x][0] = fa;
rep(i, 20) f[x][i] = f[f[x][i - 1]][i - 1];
for (auto& to : edge[x]) if (to != fa) dfs(to, x);
}
int getk(int x, int k) { //求k级祖先
if (!k) return x;
int index = log2(k); //得到最高位1的位置
//int index = log2(k & -k); 我们也可以这样得到最低位1的位置
return getk(f[x][index], k - (1 << index));
}
int main()
{
int n, m; cin >> n >> m >> s;
int root = 0;
rep(i, n) {
int p; scanf("%d", &p);
if (p) edge[p].push_back(i);
else root = i;
}
dfs(root, 0);
ll ans = 0, res = 0; //ans为最终答案, res为上一次的结果, 注意开ll, 因为会超int
rep(i, m) {
ui x = (get(s) ^ res) % n + 1, k = (get(s) ^ res) % depth[x];
res = getk(x, k);
ans ^= i * res;
}
cout << ans << endl;
return 0;
}