E. Company
给定一颗有nnn个节点的树,有mmm次询问,每次询问给定[l,r][l, r][l,r],我们可以选择删除其中的一个点ppp,然后找到一个深度最深的rtrtrt,使得剩下的点都在rtrtrt的子树上。
考虑对编号为[l,r][l, r][l,r]中的点,按照dfsdfsdfs序排序,容易想到,我们要么删除最前面的元素,要么删除最后面的元素,
我们考虑枚举这两种情况,然后只要在剩下的点中随意挑选一个点,令其不断向上跳,知道找到一个点rtrtrt,剩下的点都在rtrtrt的子树上,这里可以通过二分处理。
考虑用线段树维护最大最小值,然后从最大最小值的点向上跳,直到找到一个点其字数上落在[l,r][l, r][l,r]的值有r−l−1r - l - 1r−l−1个,然后按照要求输出最优值即可。
#include <bits/stdc++.h>using namespace std;const int N = 1e5 + 10;int head[N], to[N], nex[N], cnt = 1;int dep[N], fa[N][20], id[N], rk[N], tot, n, m;int root[N], ls[N << 6], rs[N << 6], sum[N << 6], num;int minn[N << 2], maxn[N << 2];void add(int x, int y) {to[cnt] = y;nex[cnt] = head[x];head[x] = cnt++;
}void update(int &rt, int l, int r, int x, int v) {if (!rt) {rt = ++num;}sum[rt] += v;if (l == r) {return ;}int mid = l + r >> 1;if (x <= mid) {update(ls[rt], l, mid, x, v);}else {update(rs[rt], mid + 1, r, x, v);}
}int merge(int x, int y, int l, int r) {if (!x || !y) {return x | y;}int cur = ++num;if (l == r) {sum[cur] = sum[x] + sum[y];return cur;}int mid = l + r >> 1;ls[cur] = merge(ls[x], ls[y], l, mid);rs[cur] = merge(rs[x], rs[y], mid + 1, r);sum[cur] = sum[ls[cur]] + sum[rs[cur]];return cur;
}int query_sum(int rt, int l, int r, int L, int R) {if (l >= L && r <= R) {return sum[rt];}int ans = 0, mid = l + r >> 1;if (L <= mid) {ans += query_sum(ls[rt], l, mid, L, R);}if (R > mid) {ans += query_sum(rs[rt], mid + 1, r, L, R);}return ans;
}void dfs(int rt, int f) {fa[rt][0] = f, dep[rt] = dep[f] + 1, id[rt] = ++tot, rk[tot] = rt;for (int i = 1; i < 20; i++) {fa[rt][i] = fa[fa[rt][i - 1]][i - 1];}update(root[rt], 1, n, rt, 1);for (int i = head[rt]; i; i = nex[i]) {if (to[i] == f) {continue;}dfs(to[i], rt);root[rt] = merge(root[rt], root[to[i]], 1, n);}
}void push_up(int rt) {int ls = rt << 1, rs = rt << 1 | 1;maxn[rt] = max(maxn[ls], maxn[rs]);minn[rt] = min(minn[ls], minn[rs]);
}void build(int rt, int l, int r) {if (l == r) {maxn[rt] = minn[rt] = id[l];return ;}int mid = l + r >> 1;build(rt << 1, l, mid);build(rt << 1 | 1, mid + 1, r);push_up(rt);
}pair<int, int> merge(pair<int, int> a, pair<int, int> b) {return {min(a.first, b.first), max(a.second, b.second)};
}pair<int, int> query(int rt, int l, int r, int L, int R) {if (l >= L && r <= R) {return {minn[rt], maxn[rt]};}pair<int, int> ans = {0x3f3f3f3f, -1};int mid = l + r >> 1;if (L <= mid) {ans = merge(ans, query(rt << 1, l, mid, L, R));}if (R > mid) {ans = merge(ans, query(rt << 1 | 1, mid + 1, r, L, R));}return ans;
}int k_fa(int u, int k) {for (int i = 19; i >= 0; i--) {while (k >= (1 << i)) {u = fa[u][i], k -= 1 << i;}}return u;
}int solve(int u, int L, int R) {int l = 0, r = dep[u] - 1;while (l < r) {int mid = l + r >> 1, cur = k_fa(u, mid);if (query_sum(root[cur], 1, n, L, R) >= R - L) {r = mid;}else {l = mid + 1;}}int cur = fa[u][0];return k_fa(u, l);
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);scanf("%d %d", &n, &m);for (int i = 2, x; i <= n; i++) {scanf("%d", &x);add(x, i);}dfs(1, 0);build(1, 1, n);for (int i = 1, l, r; i <= m; i++) {scanf("%d %d", &l, &r);pair<int, int> cur = query(1, 1, n, l, r);int ans1 = solve(rk[cur.first], l, r), ans2 = solve(rk[cur.second], l, r);if (dep[ans1] > dep[ans2]) {printf("%d %d\n", rk[cur.second], dep[ans1] - 1);}else {printf("%d %d\n", rk[cur.first], dep[ans2] - 1);}}return 0;
}