#include<cstdio>
using namespace std;
const int NR=1e6+1;
struct VAN
{
int l,r,tmp;//l为左儿子,r为右儿子,tmp为当前点对应的点权
}Tree[NR<<4];
int n,m,tot,a[NR],Root[NR];//Root[i]为版本i的线段树根节点
void build(int &id,int l,int r)//建树,id为当前点编号
{
id=++tot;//给节点赋编号
if(l==r){//若当前节点只对应一点
Tree[id].tmp=a[l];//赋值
return ;//结束递归
}
int mid=(l+r)>>1;
build(Tree[id].l,l,mid);//建左子树
build(Tree[id].r,mid+1,r);//建右子树
}
void insert(int &id,int l,int r,int pos,int val,int now)//插入,now为版本,pos为修改点位置
{
Tree[id=++tot]=Tree[now];//使当前路径上的点等于原来版本上的对应点
if(l==r){//当前点即为修改点
Tree[id].tmp=val;//赋值
return ;//结束递归
}
int mid=(l+r)>>1;
if(pos<=mid)//若修改点位置在当前点左子树
insert(Tree[id].l,l,mid,pos,val,Tree[now].l);
else //若修改点位置在当前点右子树
insert(Tree[id].r,mid+1,r,pos,val,Tree[now].r);
}
int query(int id,int l,int r,int pos)//查询
{
if(l==r)//当前点即为查询点
return Tree[id].tmp;//返回
int mid=(l+r)>>1;
if(pos<=mid)//若查询点在左子树
return query(Tree[id].l,l,mid,pos);
else//若查询点在右子树
return query(Tree[id].r,mid+1,r,pos);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%d",a+i);
build(Root[0],1,n);//建树
for(int i=1;i<=m;++i){
int opt,v,loc,val;//opt为操作种类,v为版本,loc为修改/查询点位置,val为修改值
scanf("%d%d%d",&v,&opt,&loc);
if(opt==1){
scanf("%d",&val);
insert(Root[i],1,n,loc,val,Root[v]);//修改
}else
printf("%d\n",query(Root[i]=Root[v],1,n,loc));//查询,同时因为查询生成不变的新版本所以Root[i]=Root[v]
}
return 0;
}
acm算法之主席树(区间第k小值)
最新推荐文章于 2024-07-21 13:38:21 发布