· 博主语文体育老师教的
· 本文年龄限定 16+
· 吐槽上面 2条的都是⑨
很久以前的坑了……
因为最近刷到树上路径K大值什么的, 实在不想写XX分块树......
推荐文章 :
1、丽洁姐的《可持久化数据结构研究》
2、Seter 对 BZOJ 1901 的吐槽 - http://seter.is-programmer.com/posts/31907.html ( 仅用来剽垃圾回收代码而已......)
具体就是考虑一个单点修改的线段树, 要求询问第 K 次修改时的线段树形态.
如果对每次修改都新开一个O(nlogn) 的线段树, 很欠抽不是么> <~
所以考虑是 " 单点修改 ", 每次修改都是O(logn) 的, 即只会影响 O(logn) 个节点.
那么对于第 K 次修改, 只对需要修改的节点新开一个版本, 其余不需要修改的节点直接调用 K - 1次时的状态就可以了.
比如说对于节点 P, 修改了Right [P], 就 Right [P] = New ( Right [P] ), Left [P] = Left[P]
也许会吐槽说 “ 那这样原来的版本不也直接被覆盖了么魂淡! ” , 但考虑到修改了 Right [P], P也会被修改, 也就是说 New (P) 也是会执行的.
最后追溯到 New (Tree_Root), 只需要 Root [K] = New(Tree_Root) 就可以了. 这样询问第K次修改与询问第K - 1修改会从完全不同的 2 个树根Root [K], Rook [k - 1]开始查找.
这样相当于空间消耗只有 O((n + q)logn) 其中 q 为修改次数.
伪代码如下 :
Function New (node p)
if P IS A LEAF // p 是叶子, 直接新建.
return new(p);
new(p); //对节点 p 新开一个版本, New 和 new 不一样哦~
if CHANGE RIGHT [P]
Right [new (p)] = New (Right [p]) //修改右子树
else
Left [new (p)] = New (Left [p]) //修改左子树
return new (p)
End Function
以下是树上两点路径 K 大值的 Code ...... LCA + 主席树,
考虑对于树上权值离散化, 按权值建一颗空线段树标记为 Root [0]
之后对于节点 P, Root [P] = New (Root[father(P)])
这样 Root [P] 就相当于将从根节点到 P 路径上的所有节点权值加入后的线段树形态.
那么对于一个询问 A, B, K, 就是在线段树 Root[A] + Root[B] - Root[Lca(A, B)] - Root[father(Lca(A, B))] ( 这样可以得到将 A, B路径中所有节点权值加入后的线段树形态) 中找.
其实想理解的话还是看看丽洁姐的论文比较好……人家是语文蒟蒻.
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define mid ((l + r) >> 1)
#define gs (c < '0' || c > '9')
int getint() { int wis = 0; char c = getchar(); while (gs) c = getchar(); while (!gs) wis = wis * 10 + c - '0', c = getchar(); return wis; }
const int ys = 100010, max_i = 17;
using namespace std;
bool t[ys];
int n, m, x, y, k, sg, num, ufa, lastans;
int tot[ys * 30], left[ys * 30], right[ys * 30];
int f[ys][20], deep[ys], root[ys];
int v[ys], g[ys], p[ys];
int c[ys * 2], gv[ys], next[ys * 2];
int build(int l, int r)
{
int p = ++sg;
if (l == r) return p;
left[p] = build(l, mid);
right[p] = build(mid + 1, r);
return p;
}
int change(int q, int w, int l, int r)
{
if (l == r) { tot[++sg] = tot[w] + 1; return sg; }
int p = ++sg; tot[p] = tot[w] + 1;
if (q <= mid) left[p] = change(q, left[w], l, mid), right[p] = right[w];
else right[p] = change(q, right[w], mid + 1, r), left[p] = left[w];
return p;
}
void dfs(int z, int fa, int de)
{
root[z] = change(p[z], root[fa], 1, n); t[z] = true; f[z][0] = fa; deep[z] = de;
for (int i = 0; f[z][i]; ++i)
f[z][i + 1] = f[f[z][i]][i];
for (int x = gv[z]; x; x = next[x])
if (!t[c[x]]) dfs(c[x], z, de + 1);
}
int ask(int a, int b, int c, int d, int k)
{
int l = 1, r = n;
while (l != r)
{
if (tot[left[a]] + tot[left[b]] - tot[left[c]] - tot[left[d]] >= k)
r = mid, a = left[a], b = left[b], c = left[c], d = left[d];
else
k -= tot[left[a]] + tot[left[b]] - tot[left[c]] - tot[left[d]], l = mid + 1,
a = right[a], b = right[b], c = right[c], d = right[d];
}
return v[g[l]];
}
int lca(int x, int y)
{
if (x == y) return x;
if (deep[x] < deep[y]) swap(x, y);
for (int i = max_i; i >= 0; --i)
if ((deep[x] - deep[y]) & (1 << i))
x = f[x][i];
if (x == y) return y;
for (int i = max_i; i >= 0; --i)
if (f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
return f[x][0];
}
void add(int x, int y)
{
c[++num] = y, next[num] = gv[x], gv[x] = num;
c[++num] = x, next[num] = gv[y], gv[y] = num;
}
bool cmp(const int &a, const int &b) { return v[a] < v[b]; }
int main()
{
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
n = getint(); m = getint();
for (int i = 1; i <= n; ++i) v[i] = getint(), g[i] = i;
sort(g + 1, g + n + 1, cmp); root[0] = build(1, n);
for (int i = 1; i <= n; ++i) p[g[i]] = i;
for (int i = 1; i < n; ++i) x = getint(), y = getint(), add(x, y);
dfs(1, 0, 1);
for (int i = 1; i <= m; ++i)
{
x = getint() ^ lastans, y = getint(), k = getint(), ufa = lca(x, y);
printf("%d", lastans = ask(root[x], root[y], root[ufa], root[f[ufa][0]], k));
if (i != m) printf("\n");
}
}