【操作树/主席树】数列

【描述】
给一个空数列,有 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;
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章