这是一道模板题。
您需要维护一个序列,其中需要提供以下操作:
第 0 0 0 个版本为空序列。修改操作不会影响被修改的版本,而总是产生一个新版本。
输入格式
第一行有一个正整数 n n n 表示操作的数量。
接下来 n n n 行每行第一个正整数 o p t opt opt 表示操作的类型,后面有 3 3 3 个整数 t , k , x t, k, x t,k,x 或 2 2 2 个整数 t , k t, k t,k 表示操作的参数。
输出格式
对于每个查询操作输出一行一个数,表示查询的结果。
这是一道可持久化平衡树的板子题,但可惜它没有强制在线,用离线的方法水过了。
对于可持久化的题,由于每个版本由且仅由一个版本演变过来的,如果我们把那个版本看作新的版本的父亲,那么这就可以看作是一棵树。我们可以离线所有的操作,构建这样一棵树。对于其中的一个节点,这个节点的操作只会对它的子树产生影响。所以我们可以用一个可撤销的数据结构,在进入这个节点的子树时做其对应的操作,离开时撤销之前的操作,像线段树分治一样,这样就可以做到可持久化的效果。
而可撤销的数据结构,这里可以使用普通维护区间的Splay,好像块链也可以。在进入子树之前,将其对应操作进行修改:加入即为加入,删除时记下来删除的节点的值,然后就直接删除;撤销时,若该点为添加操作,则将其删除,若为删除操作,则进行添加,撤销当时该边的值。
代码其实也很简单。
#include
#include
using namespace std;
const int N=600005;
//由于只有修改操作才会新建版本,所以对于查询操作,直接用链表记录
struct Query {int k,id,nxt;}q[N];
struct Edge {int to,nxt;}e[N];
int h[N],cnt;
int op[N],qk[N],qx[N];
int head[N],tot;
void Add_Edge(int x,int y) {
e[++cnt]=(Edge){y,h[x]};
h[x]=cnt;
}
void Add_Query(int x,int y,int id) {
q[++tot]=(Query){y,id,head[x]};
head[x]=tot;
}
int n,qtot,ans[N],num;
//Splay基本操作
int f[N],c[N][2],v[N];
int sz[N],rt,ntot;
void pushup(int x) {sz[x]=sz[c[x][0]]+sz[c[x][1]]+1;}
void Rotate(int x) {
int y=f[x],z=f[y];
int k=c[y][1]==x,w=c[x][k^1];
if (z) c[z][c[z][1]==y]=x;
f[y]=x;f[x]=z;if (w) f[w]=y;
c[x][k^1]=y;c[y][k]=w;
pushup(y);
pushup(x);
}
void Splay(int x,int to=0) {
if (!x) return;
for (int y,z;z=f[y=f[x]],y!=to;Rotate(x))
if (z!=to) Rotate((c[y][0]==x)^(c[z][0]==y)?x:y);
if (!to) rt=x;
}
int Find(int k) {
int p=rt;
while (p) {
if (sz[c[p][0]]+1==k) {
Splay(p);
return p;
}
if (sz[c[p][0]]>=k) p=c[p][0];
else k-=sz[c[p][0]]+1,p=c[p][1];
}
return 0;
}
void Insert(int k,int val) {
if (k==1) {
int x=Find(1);
Splay(x);ntot++;v[ntot]=val;
sz[ntot]=1;f[ntot]=x;c[x][0]=ntot;
if (!x) rt=ntot;
else pushup(rt);
return;
}
if (k==sz[rt]+1) {
int x=Find(sz[rt]);
Splay(x);ntot++;v[ntot]=val;
sz[ntot]=1;c[x][1]=ntot;f[ntot]=x;
if (!x) rt=ntot;
else pushup(x);
return;
}
int x=Find(k-1),y=Find(k);
Splay(x);Splay(y,x);ntot++;
v[ntot]=val;sz[ntot]=1;c[c[x][1]][0]=ntot;
f[ntot]=c[x][1];
pushup(c[x][1]);
pushup(x);
}
void Delete(int k) {
if (k==1) {
int x=Find(1);
Splay(x);rt=c[x][1];
f[c[x][1]]=0;c[x][1]=0;
return;
}
if (k==sz[rt]) {
int x=Find(sz[rt]);
Splay(x);rt=c[x][0];
f[c[x][0]]=0;c[x][0]=0;
return;
}
int x=Find(k),y=Find(k-1);
Splay(x);
Splay(y,x);
f[c[x][0]]=0;
f[c[x][1]]=c[x][0];
c[c[x][0]][1]=c[x][1];
rt=c[x][0];
pushup(c[x][0]);
}
int Get(int k) {return v[Find(k)];}
//End
void Solve(int x) {
int dt;
if (op[x]==1) Insert(qk[x],qx[x]);//直接改
else dt=Get(qk[x]), Delete(qk[x]);
for (int i=head[x];i;i=q[i].nxt)
ans[q[i].id]=Get(q[i].k);
for (int i=h[x];i;i=e[i].nxt)
Solve(e[i].to);
if (op[x]==1) Delete(qk[x]);//撤销
else Insert(qk[x],dt);
}
int main() {
scanf("%d",&n);
for (int i=1;i<=n;i++) {
int opr,t,k,x;
scanf("%d%d%d",&opr,&t,&k);
if (opr==1) scanf("%d",&x);
if (opr==1) {//1,2为修改操作,要新建版本,它继承的版本向它连边
qtot++;op[qtot]=opr;
qk[qtot]=k;qx[qtot]=x;
Add_Edge(t,qtot);
} else if (opr==2) {
qtot++;op[qtot]=opr;
qk[qtot]=k;
Add_Edge(t,qtot);
} else if (opr==3) Add_Query(t,k,++num);//3为查询操作,直接加入链表里
}
Solve(0);
for (int i=1;i<=num;i++)
printf("%d\n",ans[i]);
return 0;
}