Description
给你两棵有 (N) 个点的树,保证每个点的父亲的编号比自己小。每次询问给出 (p_1, p_2) ,设 (S_1) 为第一棵树中 (p_1) 到根的路径上的点的编号的集合, (S_2) 为第二棵数中 (p_2) 到根的路径上的点的编号的集合,设 (p_3) 为 (S_1cap S_2) 中的最大元素,求 (p_3) 以及在第一棵树中 (p_1,p_3) 的距离和第二棵树中 (p_2,p_3) 的距离。
Input
第一行两个正整数 (N,M) 代表点的个数和询问组数。
第二行 (N-1) 个数,代表第一棵树中 (2) 号点到 (N) 号点的父亲。
第三行 (N-1) 个数,代表第二棵树中 (2) 号点到 (N) 号点的父亲。
接下来 (M) 行,每行两个数 (p_1,p_2) 代表一组询问。这里的 (p_1,p_2) 均需要解密,如果设上次询问的答案为 (p_3) (如果是第一次询问,则 (p_3=0) ),那么我们需要计算 (p_1=(p_1 + p_3) mod N + 1,p_2=(p_2+p_3) mod N+1) 来得到实际的 (p_1,p_2) 。
Output
(M) 行,每行三个数代表答案。
Sample
Sample Input 1
5 1
1 2 3 3
1 1 3 2
4 3
Sample Output 1
3 2 2
Sample Input 2
5 3
1 1 2 2
1 2 2 1
5 3
5 4
3 5
Sample Output 2
1 1 3
1 2 1
2 2 1
Sample Input 3
5 3
1 1 2 2
1 2 3 1
1 4
1 1
3 4
Sample Output 4
1 2 2
3 1 1
2 1 2
Limit
对于 (50\%) 的数据, (1le N,Mle 10^3) 。
对于 (100\%) 的数据, (1le N,Mle 10^5) 。
Solution
这题的主席树做法太妙了!
以第一棵树的父子关系建线段树,以第二棵树的 (mathrm{dfs}) 序作为区间。
什么意思呢?
比如,第二棵树里的点 (3) 的 (mathrm{dfs}) 区间为 ([2,6]) ,我们就在第一棵树中以 (3) 的父亲作为基础建主席树,把 ([2,6]) 全部赋值为 (3) 就可以了。
啊……好难讲啊(我太菜了),还是看代码好理解。
#include<bits/stdc++.h>
using namespace std;
#define N 100001
#define rep(i, a, b) for (int i = a; i <= b; i++)
inline int read() {
int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}
int n, m, head[N], tot = 1, fa[2][N], dep[2][N], l[N], r[N], ind;
struct edge { int v, next; }e[N];
inline void add(int u, int v) { e[++tot].v = v, e[tot].next = head[u], head[u] = tot; }
void dfs(int t, int u) {
if (t) l[u] = ++ind;
for (int i = head[u]; i; i = e[i].next) dep[t][e[i].v] = dep[t][u] + 1, dfs(t, e[i].v);
if (t) r[u] = ind;
}
struct node { int val, ls, rs; }T[N * 40];
int root[N], ndCnt;
#define mid (l + r >> 1)
int modify(int pre, int l, int r, int L, int R, int val) {
int now = ++ndCnt; T[now] = T[pre];
if (l >= L && r <= R) { T[now].val = val; return now; }
if (L <= mid) T[now].ls = modify(T[pre].ls, l, mid, L, R, val);
if (R > mid) T[now].rs = modify(T[pre].rs, mid + 1, r, L, R, val);
return now;
}
int query(int rt, int l, int r, int pos) {
if (!rt) return 0;
int ret = T[rt].val;
if (pos <= mid) ret = max(ret, query(T[rt].ls, l, mid, pos));
else ret = max(ret, query(T[rt].rs, mid + 1, r, pos));
return ret;
}
int main() {
n = read(), m = read();
rep(i, 2, n) fa[0][i] = read();
rep(i, 2, n) { fa[1][i] = read(); add(fa[1][i], i); }
dep[1][1] = 1, dfs(1, 1);
memset(head, 0, sizeof head); tot = 1; rep(i, 2, n) add(fa[0][i], i);
dep[0][1] = 1, dfs(0, 1);
rep(i, 1, n) root[i] = modify(root[fa[0][i]], 1, n, l[i], r[i], i);
int ans = 0;
while (m--) {
int p1 = (read() + ans) % n + 1, p2 = (read() + ans) % n + 1;
ans = query(root[p1], 1, n, l[p2]);
printf("%d %d %d
", ans, dep[0][p1] - dep[0][ans] + 1, dep[1][p2] - dep[1][ans] + 1);
}
return 0;
}