题目传送门:洛谷P3835。
题意简述:
题面说的很清楚了。
题解:
考虑建立一棵每个节点都表示一个版本的树。
以初始版本 \(0\) 为根。对于第 \(i\) 个操作,从 \(v_i\) 向 \(i\) 连一条边,而边权则是 \(opt_i\) 和 \(x_i\) 的二元组,表示经过这条边上操作,可以达到下一个状态。
考虑使用权值树状数组维护操作。只需要实现单点加,查询前缀和以及树状数组上二分的操作即可。
树状数组提前插入 \(-2147483647\) 和 \(2147483647\) 两个数,方便统计。
因为权值范围太大,所以先离散化权值,再插入树状数组。
只需要从结点 \(0\) 开始 DFS ,进入子树时执行操作,退出子树时撤销操作即可。
#include <cstdio>
#include <algorithm>
using namespace std; const int INF = 0x7fffffff;
const int MQ = ; int N, Q;
int faz[MQ], opt[MQ], a[MQ], b[MQ];
int Ans[MQ]; int eh[MQ], nxt[MQ], to[MQ], tot;
inline void ins(int x, int y) {
nxt[++tot] = eh[x]; to[tot] = y; eh[x] = tot;
} int B[MQ];
inline void Add(int i, int x) { for (; i <= N; i += i & -i) B[i] += x; }
inline int Qur(int i) { int A = ; for (; i; i -= i & -i) A += B[i]; return A; }
inline int BS(int x) { int p = ; for (int j = << ; j; j >>= ) if ((p | j) <= N && B[p | j] <= x) x -= B[p |= j]; return p;} void DFS(int u, int o, int x) {
int ok = ;
if (o == ) Add(x, );
if (o == ) {
if (Qur(x) == Qur(x - )) ok = ;
else Add(x, -);
}
if (o == ) Ans[u] = Qur(x - );
if (o == ) Ans[u] = b[BS(x) + ];
if (o == ) Ans[u] = b[BS(Qur(x - ) - ) + ];
if (o == ) Ans[u] = b[BS(Qur(x)) + ]; for (int i = eh[u]; i; i = nxt[i])
DFS(to[i], opt[to[i]], a[to[i]]); if (o == ) Add(x, -);
if (o == && ok) Add(x, );
} int main() {
scanf("%d", &Q);
for (int i = ; i <= Q; ++i) {
scanf("%d%d%d", &faz[i], &opt[i], &a[i]);
if (opt[i] != )
b[++N] = a[i];
} b[++N] = -INF, b[++N] = INF;
sort(b + , b + N + );
N = unique(b + , b + N + ) - b - ;
for (int i = ; i <= Q; ++i) {
ins(faz[i], i);
if (opt[i] != )
a[i] = lower_bound(b + , b + N + , a[i]) - b;
}
Add(, ), Add(N, );
DFS(, , );
for (int i = ; i <= Q; ++i) {
if(opt[i] > )
printf("%d\n", Ans[i]);
}
return ;
}