题目
有一个初始为空的数组,你需要处理q(q<=1e6)次操作,操作分四种:
① + x,数组后面加一个新的数x
② - k,删掉数组最后面的k个值
③ !,回滚最后一次变更(只有①操作和②操作视为变更)
④?,询问当前数组内的不同的数的个数(即数的种类数)
E1允许离线,E2强制在线,需要每次输出答案后刷新缓冲区
思路来源
jjleo代码/jiangly代码/官方题解
心得
这个题赛中一看,这不主席树sb题,结果写了一发喜提MLE
然后以为卡卡空间就卡过去了,结果卡了6个小时空间才卡过去,喜提-200分掉到蓝名成就
画风


3s、256M,可以说是非常极限了(不过还没有加快读)
最后卡过去的提交是,考虑到数字都在,所以,
用三个unsigned char和一位的bitset模拟了下int,
大概把空间大概压到了3/4(感觉纯纯脑瘫)
题解
主席树
朴素
 
1. 加数时,按照询问动态开点,在主席树上建一条链
2. 查询时,就全局查询数的种类数,实际只需要用到根节点
3. 回滚时,维护一个数组记录一下操作的点号的序列,退到前一个
4. 删除时,倍增往上跳k个
主席树需要维护lson、rson、num三个变量,num从0变1表示新增,其余非新增,
每个空间1e6*20大概2e7,三个6e7,倍增也需要开1e6*20=2e7,
这样做空间总共约8e7,而256M空间只能开下约6e7的数组
优化
不需要num这个变量
当主席树上建一条新链的时候,在进入叶子结点前,
先判断一下叶子结点这个方向的点是否存在,
已经存在就说明种类数没有新增,否则是新增了一个值
而询问只需要全局询问根,所以只需要每个节点记录一下答案
从而将1e6*20的空间优化到了1e6
树状数组
待补
差分+前缀和O(n)
待补
代码
主席树优化
#include <bits/stdc++.h>
#define maxn 1000086
using namespace std;
int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch > '9' || ch < '0'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch -'0';ch = getchar();}
    return x * f;
}
struct Node{
	int son[2];
}t[maxn * 21];
#define ls(x) (t[x].son[0])
#define rs(x) (t[x].son[1])
int cnt;
int modify(int x, int l, int r, int pos){
	int tag = 0;
	while(l < r){
		int mid = l + r >> 1;
		if(mid >= pos){
			if(!ls(x)) tag = 1;
			t[++cnt] = t[ls(x)], x = ls(x) = cnt, r = mid;
		}else{
			if(!rs(x)) tag = 1;
			t[++cnt] = t[rs(x)], x = rs(x) = cnt, l = mid + 1;
		} 
	}
	return tag;
}
int q;
int fa[maxn][20];
char s[10];
int tot = 1;
int a[maxn], siz;
int ans[maxn], rt[maxn];
int main(){
	q = read();
	int now = 1;
	while(q--){
		scanf("%s", s);
		if(s[0] == '+'){
			int x;
			x = read();
			int p = ++tot;
			fa[p][0] = now;
			for(int i = 1;i < 20;i++) fa[p][i] = fa[fa[p][i - 1]][i - 1];
			rt[p] = ++cnt;
			t[rt[p]] = t[rt[now]];
			ans[p] = ans[now] + modify(rt[p], 1, 1e6, x);
			now = p;
			a[++siz] = now;
		}else if(s[0] == '-'){
			int k;
			k = read();
			for(int i = 0;i < 20;i++) if(k & (1 << i)) now = fa[now][i];
			a[++siz] = now;
		}else if(s[0] == '!'){
			now = a[--siz];
		}else{
			printf("%d\n", ans[now]), fflush(stdout);
		}
	}
} 
主席树朴素(空间卡常)
 
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,ll> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=1e6+10,M=N*22,INF=0x3f3f3f3f,lb=(1<<8)-1,mx=1<<24;
int q,x,y,cur,par[N][10],up=1000000;
int root[N],pos;
int op[N],c;
char s[5];
bitset<M>most[2];
struct node{
	unsigned char a[3];
	int g(){
		int f=0;
		for(int i=2;i>=0;--i){
			f=(f<<8)|a[i];
		}
		return f;
	}
	void s(int x){
		//assert(x<(1<<20));
		for(int i=0;i<3;++i){
			//printf("x&lb:%d\n",x&lb);
			a[i]=x&lb;
			x>>=8;
		}
	}
}sum[M];
node operator+(node a,node b){
	int v1=a.g(),v2=b.g();
	node c;c.s(v1+v2);
	return c;
}
struct node2{
	unsigned char a[3];
	int g(int p,int id){
		int f=0;
		for(int i=2;i>=0;--i){
			f=(f<<8)|a[i];
		}
		if(most[id].test(p))f|=mx;
		return f;
	}
	void s(int p,int id,int x){
		if(x&mx)most[id].set(p);
		else most[id].reset(p);
		for(int i=0;i<3;++i){
			//printf("x&lb:%d\n",x&lb);
			a[i]=x&lb;
			x>>=8;
		}
	}
}ch[M][2];
int copy(int x){
	pos++;
	//if(pos>=M)while(1);
	ch[pos][0]=ch[x][0];
	if(most[0].test(x))most[0].set(pos);
	ch[pos][1]=ch[x][1];
	if(most[1].test(x))most[1].set(pos);
	sum[pos]=sum[x];
	return pos;
}
void add(int k,int l,int r,int x){
	if (l==r) {if(!sum[k].g())sum[k].s(1);return;}
	int mid=(l+r)/2,y=ch[k][0].g(k,0),z=ch[k][1].g(k,1);
	if (x<=mid){
		y=copy(y);
		ch[k][0].s(k,0,y);
		add(y,l,mid,x);
	}
	else{
		z=copy(z);
		ch[k][1].s(k,1,z);
		add(z,mid+1,r,x);
	}
	sum[k]=sum[y]+sum[z];
}
int ask(int k,int l,int r,int a,int b){
	if (!k) return 0;
	if (a==l&&b==r) return sum[k].g();
	int mid=(l+r)/2;
	if (b<=mid) return ask(ch[k][0].g(k,0),l,mid,a,b);
	else if (a>mid) return ask(ch[k][1].g(k,1),mid+1,r,a,b);
	else return ask(ch[k][0].g(k,0),l,mid,a,mid)+ask(ch[k][1].g(k,1),mid+1,r,mid+1,b);
}
void op1(int x){
	int las=cur;
	cur=++y;
	par[cur][0]=las;
	root[cur]=copy(root[las]);
	rep(i,1,9){
		int z=cur;
		for(int j=1;j<=4;++j){
			z=par[z][i-1];
		}
		par[cur][i]=z;
	}
	add(root[cur],1,up,x);
	op[++c]=las;
}
void op2(int k){
	int las=cur;
	for(int i=k,x=0;i;i>>=2,x++){
		int v=i&3;
		for(int j=1;j<=v;++j){
			cur=par[cur][x];
		}
	}
	//printf("cur:%d\n",cur);
	op[++c]=las;
}
void op3(){
	cur=op[c--];
	//printf("cur:%d\n",cur);
	return;
}
void op4(){
	printf("%d\n",sum[root[cur]].g());
	fflush(stdout);
}
int main(){
	//sum[0].s(125);
	//printf("%d\n",sum[0].g());
	sci(q);
	root[0]=pos=1;
	while(q--){
		scanf("%s",s);
		if(s[0]=='+'){
			sci(x);
			op1(x);
		}
		else if(s[0]=='-'){
			sci(x);
			op2(x);
		}
		else if(s[0]=='!'){
			op3();
		}
		else{
			op4();
		}
	}
	return 0;
} 
                














![[保研/考研机试] KY11 二叉树遍历 清华大学复试上机题 C++实现](https://img-blog.csdnimg.cn/7caeb35fef9748e088f020f524d4b9f4.png)



