描述
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
分析
- 想用树链剖分但是发现不知道怎么套主席树.
- 正解是LCA, 只要维护根节点到每个结点u的权值线段树. 因为主席树求第k大支持减法, 所以最终答案就是线段树 u+v-lca(u,v)-fa[lca(u,v)] 中的第k大.
- 调了半天发现在用lower_bound查找离散后的权值时, unique后的T数组有序不假但重复元素被挪到了数组尾, 所以不能再用原来的T+n+1作为二分查找的上界了.
#include
#include
#include
using namespace std;
const int maxn = 100000 + 10;
const int maxm = 3000000;
const int DEP = 20;
vector
G[maxn];
int n, m, tot, node_cnt;
int A[maxn], T[maxn];
int dep[maxn], fa[maxn][DEP];
int root[maxm], lc[maxm], rc[maxm], s[maxm];
#define M (L+R>>1)
void modify(int& x, int y, int L, int R, int d) {
x = ++node_cnt;
lc[x] = lc[y]; rc[x] = rc[y]; s[x] = s[y] + 1;
if(L == R) return;
if(d <= M) modify(lc[x], lc[y], L, M, d);
else modify(rc[x], rc[y], M+1, R, d);
}
int query(int u, int v, int x, int y, int L, int R, int k) {
if(L == R) return L;
int ls = s[lc[u]] + s[lc[v]] - s[lc[x]] - s[lc[y]];
if(k <= ls) return query(lc[u], lc[v], lc[x], lc[y], L, M, k);
return query(rc[u], rc[v], rc[x], rc[y], M+1, R, k-ls);
}
void dfs(int u, int x) {
dep[u] = dep[fa[u][0] = x] + 1;
for(int i = 1; i < DEP; i++)
if((1<
<= dep[u]) fa[u][i] = fa[fa[u][i-1]][i-1]; else break; modify(root[u], root[x], 1, tot, A[u]); for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(v != x) dfs(v, u); } } int lca(int x, int y) { if(dep[x] < dep[y]) swap(x, y); int delta = dep[x]-dep[y]; for(int i = 0; i < DEP; i++) if(delta&(1<
= 0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } int main() { int n, m; scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++) { scanf("%d", &A[i]); T[i] = A[i]; } sort(T+1, T+n+1); tot = unique(T+1, T+n+1) - T-1; for(int i = 1; i <= n; i++) A[i] = lower_bound(T+1, T+tot+1, A[i]) - T; for(int i = 1; i < n; i++) { int u, v; scanf("%d %d", &u, &v); G[u].push_back(v); G[v].push_back(u); } dfs(1, 0); int ans = 0; for(int i = 1; i <= m; i++) { int u, v, k; scanf("%d %d %d", &u, &v, &k); u ^= ans; int x = lca(u, v), y = fa[x][0]; ans = T[query(root[u], root[v], root[x], root[y], 1, tot, k)]; if(i < m) printf("%d\n", ans); else printf("%d", ans); } return 0; }