【描述】
給一個空數列,有 M 次操作,每次操作是以下三種之一:
(1)在數列後加一個數
(2)求數列中某位置的值
(3)撤銷掉最後進行的若干次操作(1 和 3)
【思路】
這是一道主席樹板子題。但是注意到這裏只涉及單點操作,所以我們實際上並不需要主席樹的樹形結構進行區間維護。我們考慮維護一棵操作樹,節點到根的路徑表示當前序列:
初始時操作樹爲空。
對於一操作:我們在當前節點下新建一個兒子。
對於二操作:我們可以維護當前點的深度,倍增求k級祖先。
對於三操作:直接回溯到之前某個點。
代碼:
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=2e5+5;
inline int red(){
int data=0;bool w=0;char ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=1,ch=getchar();
while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return w?-data:data;
}
int n;
char s[3];
int fa[N][19],dep[N],val[N],now=0,tot=0,pre[N],rt[N],cnt=0;
inline void init(int p){
for(int re i=1;(1<<i)<=dep[p];i++)
fa[p][i]=fa[fa[p][i-1]][i-1];
}
inline int query(int x,int t){
for(int re i=0;(1<<i)<=t;i++)
if(t&(1<<i))x=fa[x][i];
return val[x];
}
int main(){
n=red();
while(n--){
scanf("%s",s);
switch(s[0]){
case 'A':fa[++tot][0]=now,dep[rt[++cnt]=tot]=dep[now]+1,val[tot]=red(),init(now=tot);break;
case 'Q':cout<<query(now,dep[now]-red())<<"\n";break;
case 'U':++cnt,now=rt[cnt]=rt[cnt-1-red()];break;
}
}
}