ming 茗
Description
给你两棵有 \(N\) 个点的树,保证每个点的父亲的编号比自己小。每次询问给出 \(p_1, p_2\) ,设 \(S_1\) 为第一棵树中 \(p_1\) 到根的路径上的点的编号的集合, \(S_2\) 为第二棵数中 \(p_2\) 到根的路径上的点的编号的集合,设 \(p_3\) 为 \(S_1\cap 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) \bmod N + 1,p_2=(p_2+p_3) \bmod 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\%\) 的数据, \(1\le N,M\le 10^3\) 。
对于 \(100\%\) 的数据, \(1\le N,M\le 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\n", ans, dep[0][p1] - dep[0][ans] + 1, dep[1][p2] - dep[1][ans] + 1);
}
return 0;
}