【操作樹/主席樹】數列

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