P1486 [NOI2004]郁闷的出纳员

皎月半洒花

2018-04-28 21:16:41

Personal

这个题算是平衡树系列的一个进阶版本了吧$qwq$. 好吧我承认,这个题我一开始做的时候已经想出了大约$60$%的样子,但是最后还是偷偷地瞅了眼题解,发现似乎很有道理,然后$A$了$emmmm$. 那么好像很显然,我们坑定不能一个一个地修改,区间修改由于每次都是$1$~$n$,所以并没有什么很大的意义。所以我们可以考虑引入一个标记,每次$A$就$$delta+=num$$每次$I$就$$insert(num-delta)$$,询问时就$$cout<<query+delta$$,再加上几个判断是不是非法操作。 $emmmmm$好像海星,到这一步大约已经可以够得着$NOIP$的思维难度了。然而这是一道省选题,所以这么搞还是捕星,因为删除操作和询问离职人员好像很难搞。 那么,在这里我们考虑将$-INF$和$INF$在一开始就插入平衡树里。每次$insert$操作$tot++$,最后只需要用$$tot-(find\_rank(INF)-2)$$就可以算出离职人数。 那么只剩下最后一个问题了——我们该怎么删除呢?这里就需要用到一个二叉搜索树里很精妙的操作了——删除根的子树。我们可以在执行$S$操作时先$delta-=num$,然后将$-INF$旋转到根节点,将$minn-delta$旋转到根节点的右儿子,然后删除根节点的右子树的左子树即可。 诶这个操作很熟悉啊,不也是区间翻转时的操作吗? 诶怎么旋转$minn-delta$啊? 我们可以很简单地插入删除,这很简单,但是为什么要以$minn-delta$作为所删除的单调区间的上界?这个地方你可以稍微意会一下……**因为我们插入的是$num-delta$啊!** 嗯,我还是太弱了。 # $\color{red}Code:$ ```cpp #include<iostream> #include<cstdio> using namespace std; #define MAXN 1000000 #define INF 283653129 int f[MAXN],cnt[MAXN],value[MAXN],sons[MAXN][2],sub_size[MAXN],whole_size,root; inline int qread(){ int res=0,k=1; char c=getchar(); while(!isdigit(c)){ if(c=='-')k=-1; c=getchar(); } while(isdigit(c)){ res=(res<<1)+(res<<3)+c-48; c=getchar(); } return res*k; } inline void S_Clear(int x){ sons[x][0]=sons[x][1]=f[x]=sub_size[x]=cnt[x]=value[x]=0; } inline bool get_which(int x){ return sons[f[x]][1]==x; } inline void update(int x){ if (x){ sub_size[x]=cnt[x]; if (sons[x][0]) sub_size[x]+=sub_size[sons[x][0]]; if (sons[x][1]) sub_size[x]+=sub_size[sons[x][1]]; } return ; } inline void rotate(int x){ int father=f[x],g_father=f[father],which_son=get_which(x); sons[father][which_son]=sons[x][which_son^1]; f[sons[father][which_son]]=father; sons[x][which_son^1]=father; f[father]=x; f[x]=g_father; if(g_father){ sons[g_father][sons[g_father][1]==father]=x; } update(father); update(x); } inline void splay(int x,int goal){ for(int qwq;(qwq=f[x])!=goal;rotate(x)){ if(f[qwq]!=goal){ rotate(get_which(x)==get_which(qwq)?qwq:x); } } if(!goal){ root=x; } } inline void insert(int x){ if(!root){ whole_size++; sons[whole_size][0]=sons[whole_size][1]=f[whole_size]=0; root=whole_size; sub_size[whole_size]=cnt[whole_size]++; value[whole_size]=x; return ; } int now=root,fa=0; while(1){ if(x==value[now]){ cnt[now]++; update(now); update(fa); splay(now,0); break; } fa=now; now=sons[now][value[now]<x]; if(!now){ whole_size++; sons[whole_size][0]=sons[whole_size][1]=0; f[whole_size]=fa; sub_size[whole_size]=cnt[whole_size]=1; sons[fa][value[fa]<x]=whole_size; value[whole_size]=x; update(fa); splay(whole_size,0); break; } } } inline int find_num(int x){ int now=root; while(1){ if(sons[now][0]&&x<=sub_size[sons[now][0]]) now=sons[now][0]; else { int temp=(sons[now][0]?sub_size[sons[now][0]]:0)+cnt[now]; if(x<=temp)return value[now]; x-=temp; now=sons[now][1]; } } } inline int find_ID(int x){ int now=root; while(1){ if(x==value[now]){ return now; } else now=sons[now][value[now]<x]; } } inline int find_rank(int x){ int now=root,ans=0; while(1){ if (x<value[now]) now=sons[now][0]; else{ ans+=(sons[now][0]?sub_size[sons[now][0]]:0); if (x==value[now]){ splay(now,0); return ans+1; } ans+=cnt[now]; now=sons[now][1]; } } } inline int find_pre(){ int now=sons[root][0]; while(sons[now][1])now=sons[now][1]; return now; } inline int find_suffix(){ int now=sons[root][1]; while(sons[now][0])now=sons[now][0]; return now; } inline void my_delete(int x){ int hhh=find_rank(x); if (cnt[root]>1){ cnt[root]--; update(root); return; } if (!sons[root][0]&&!sons[root][1]) { S_Clear(root); root=0; return; } if (!sons[root][0]){ int old_root=root; root=sons[root][1]; f[root]=0; S_Clear(old_root); return; } else if (!sons[root][1]){ int old_root=root; root=sons[root][0]; f[root]=0; S_Clear(old_root); return; } int left_max=find_pre(),old_root=root; splay(left_max,0); sons[root][1]=sons[old_root][1]; f[sons[old_root][1]]=root; S_Clear(old_root); update(root); } int main(){ int m,num,minn; char a; cin>>m>>minn; insert(-INF); insert(INF); int delta=0,sumtot=0; for(int i=1;i<=m;i++){ cin>>a>>num; switch(a){ case 'I' :{ if(num<minn)break; insert(num-delta); sumtot++; break; } case 'A':{ delta+=num; break; } case 'S':{ delta-=num; insert(minn-delta); int a=find_ID(-INF),b=find_ID(minn-delta); splay(a,0); splay(b,a); sons[sons[root][1]][0]=0; my_delete(minn-delta); break; } case 'F':{ int sumnow=find_rank(INF)-2; if(sumnow<num){ cout<<-1<<endl; break; } int res=find_num(sumnow+2-num); cout<<res+delta<<endl; break; } } } int sumnow=find_rank(INF)-2; cout<<sumtot-sumnow; return 0; } ``` 代码好长啊……足足写了5.04K