Codeforces Round 890 (Div. 2) E2. PermuTree (hard version) (主席树/树状数组/差分+前缀和)

题目

有一个初始为空的数组,你需要处理q(q<=1e6)次操作,操作分四种:

① + x,数组后面加一个新的数x

② - k,删掉数组最后面的k个值

③ !,回滚最后一次变更(只有①操作和②操作视为变更)

④?,询问当前数组内的不同的数的个数(即数的种类数)

E1允许离线,E2强制在线,需要每次输出答案后刷新缓冲区

思路来源

jjleo代码/jiangly代码/官方题解

心得

这个题赛中一看,这不主席树sb题,结果写了一发喜提MLE

然后以为卡卡空间就卡过去了,结果卡了6个小时空间才卡过去,喜提-200分掉到蓝名成就

画风

3s、256M,可以说是非常极限了(不过还没有加快读)

最后卡过去的提交是,考虑到数字都在[0,2^{25}),所以,

用三个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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值