题目链接:https://www.luogu.org/problemnew/show/P3919
主席树模板题,学了下主席树的模板
主席树的核心思想是每次储存下来每次单点修改之后的线段树的数据,但这样肯定会mle,所以有一个优化的方法:因为每次只更新一个点,那么在这个线段树里只有这个点所在的那一条链可能会修改,也就是说我们只要记录这一条链就足够了,其他的数据我们只要记录这个版本是从哪个版本修改而来即可,空间复杂度是(4 + n)logn。
#include<bits/stdc++.h>
using namespace std;
const int maxn = (int) (1e6 + 5);
int a[maxn],n,rt[maxn]; //rt为版本号
struct pstree
{
struct pst
{
int lch,rch,val;
}z[maxn];
int cnt; // 节点个数
inline void build(int &p, int l, int r) // 建树
{
p = ++cnt; // 版本号
if (l == r)
{
z[p].val = a[l];
return;
}
int mid = (l + r) >> 1;
build(z[p].lch, l, mid); // 左儿子是哪个版本
build(z[p].rch, mid + 1, r); // 右儿子是哪个版本
}
inline void insert(int &p, int pre, int l, int r, int q, int v) // 更新后插入
{
p = ++cnt; // 版本号
z[p] = z[pre]; // 将当前版本初始化与前一个版本相同
if (l == r)
{
z[p].val = v;
return;
}
int mid=(l + r) >> 1;
if (q <= mid)
insert(z[p].lch, z[pre].lch, l, mid, q, v); //更新左儿子
else
insert(z[p].rch, z[pre].rch, mid + 1, r, q, v); // 更新右儿子
}
inline int query (int p, int l, int r, int q) // 查找
{
if (l == r)
return z[p].val;
int mid=(l + r) >> 1;
if (q <= mid)
return query(z[p].lch, l, mid, q); // 在左儿子中找
else
return query(z[p].rch, mid + 1, r, q); // 在右儿子中找
}
}pstree;
int main()
{
// freopen("in.txt", "r", stdin);
int m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
pstree.build(rt[0], 1, n);
for (int i = 1; i <= m; ++i)
{
int p,f,x;
scanf("%d%d%d", &p, &f, &x);
if (f == 1)
{
int v;
scanf("%d", &v);
pstree.insert(rt[i], rt[p], 1, n, x, v);
}
if (f == 2)
{
int tmp = pstree.query(rt[p], 1, n, x);
printf("%d\n", tmp);
rt[i] = rt[p];
}
}
}