题目
有一个初始为空的数组,你需要处理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;
}