题目链接如下:
主要是要对区间操作和这种splay树的性质比较清楚。
关于区间我们设立两个额外节点,用来设立最开始的左右区间。
性质方面,其实就是二叉搜索树的性质,这里的体现就是中序遍历就是顺时针访问输入数据,逆中序就是逆时针。
以下是思路:
我们对每一组输入,首先创建两个节点,用来表示最左最右区间,这样对于后边限定范围十分有帮助。
有几个比较有意思的操作:
在这里的建树就是建立一颗二叉搜索树,这个搜索的key是针对输入顺序来说的,而每个树节点又需要表示一些额外的信息比如前驱,孩子节点,以及存储的输入等等。这里还得存储翻转和加标记,一遍后续旋转过程中动态更新。
所以我们最初建立的树是长这样的:
其实这应该是区间操作的核心,就是这个二叉搜索树是左端点和右端点之间的。
要给顺时针的k2个数加上一个数x,那么我们也就是要从当前指针指向的节点tp往后中序遍历一共k2个几点,把这k2个节点进行加一个数的操作。我们首先把左区间旋转到根的位置,然后把当前指针指向节点旋转到根下面,而这个节点的左边的子树,也就是按照顺时针访问的最后几个元素(逆时针开始的几个),把他给砍掉,再把最右节点的前一个节点转到根,接着把刚刚砍掉的左子树放到最后一个节点的左子树上,也就是当前根节点的右孩子的左孩子上面,这样,在中序遍历的时候,就已经变成了以指针指向的节点为起点,顺时针直到访问到最后一个节点。
这个时候我们还要给当前指针指向的节点到第k2个数都加上一个数x对吧,那么还是老办法,把左区间端点转到根,把第k2+2个数转到root下面,那么此时root的左子树就是[2,k2+1]次序的数,我们把这个树的根打上加的标记,在后续过程动态更新,那么就完成了我们的操作。
而add操作明白了之后,后续的区间操作也就大同小异。
代码如下所示:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- using namespace std;
-
- #define keyv tree[tree[root][1]][0]
- #define lt(r) tree[r][0]
- #define rt(r) tree[r][1]
-
- const int MAXN=2e5+1;
- int pre[MAXN],sz[MAXN],rev[MAXN],add[MAXN],tree[MAXN][2],key[MAXN];
- int root,tot1,tot2;
- int s[MAXN],a[MAXN];
- int n,q,k1,k2;
- int tp;
-
- void newnode(int &r,int fa,int k){
- if (tot2) r=s[tot2--];
- else r=++tot1;
- key[r]=k;
- pre[r]=fa;
- add[r]=rev[r]=lt(r)=rt(r)=0;
- sz[r]=1;
- }
-
- void update_rev(int r){
- if (!r) return;
- swap(lt(r), rt(r));
- rev[r]^=1;
- }
-
- void update_add(int r,int v){
- if (!r) return;
- key[r]+=v;
- add[r]+=v;
- }
-
- void pushup(int r){
- sz[r]=sz[lt(r)]+sz[rt(r)]+1;
- }
-
- void pushdown(int r){
- if (rev[r]){
- update_rev(lt(r));
- update_rev(rt(r));
- rev[r]=0;
- }
- if (add[r]){
- update_add(lt(r),add[r]);
- update_add(rt(r),add[r]);
- add[r]=0;
- }
- }
-
- void build(int &x,int l,int r,int fa){
- if (l>r) return;
- int mid=(l+r)>>1;
- newnode(x,fa,a[mid]);
- build(lt(x),l,mid-1,x);
- build(rt(x),mid+1,r,x);
- pushup(x);
- }
-
- void init(){
- root=tot1=tot2=0;
- pre[root]=sz[root]=rev[root]=add[root]=key[root]=lt(root)=rt(root)=0;
- newnode(root,0,-1);
- newnode(rt(root),root,-1);
- for (int i = 0; i < n; ++i) {
- scanf("%d",&a[i]);
- }
- // cout<
- build(keyv,0,n-1,rt(root));
- pushup(rt(root));
- pushup(root);
- }
-
- void rotate(int x,int d){
- int y=pre[x];
- pushdown(y);
- pushdown(x);
- tree[y][!d]=tree[x][d];
- pre[tree[x][d]]=y;
- if (pre[y]){
- tree[pre[y]][rt(pre[y])==y]=x;
- }
- pre[x]=pre[y];
- tree[x][d]=y;
- pre[y]=x;
- pushup(y);
- }
-
- void splay(int r,int goal){
- pushdown(r);
- while (pre[r]!=goal){
- if (pre[pre[r]]==goal){
- pushdown(pre[r]);
- pushdown(r);
- rotate(r, lt(pre[r])==r);
- }else{
- pushdown(pre[pre[r]]);
- pushdown(pre[r]);
- pushdown(r);
- int y=pre[r];
- int d=(lt(pre[y])==y);
- if (tree[y][d]==r){// 不共线
- rotate(r,!d);
- rotate(r,d);
- }else{
- rotate(y,d);
- rotate(r,d);
- }
- }
- }
- pushup(r);
- if (goal==0) root=r;
- }
-
- int getkth(int r,int k){
- pushdown(r);
- int t=sz[lt(r)]+1;
- if (k==t) return r;
- else if (k
return getkth(lt(r),k); - else return getkth(rt(r),k-t);
- }
-
- void ADD(int x){
- // 将当前指针指向的位置的左子树去除
- splay(tp,0);
- int tmp=sz[lt(tp)]+1;
- splay(getkth(root,1),0);
- splay(getkth(root,tmp),root);
- tmp=keyv;
- keyv=0;
- pushup(rt(root));
- pushup(root);
-
- // 将去除的那棵左子树插入到末尾
- splay(getkth(root,sz[root]-1),0);
- keyv=tmp;
- pre[keyv]=rt(root);
- pushup(rt(root));
- pushup(root);
-
- // 打上加号
- splay(getkth(root,1),0);
- splay(getkth(root,k2+2),root);
- update_add(keyv,x);
- pushup(rt(root));
- pushup(root);
-
- tp= getkth(root,2);
- }
-
- void REVERSE(){
- // 将当前指针指向的位置的左子树去除
- splay(tp,0);
- int tmp=sz[lt(tp)]+1;
- splay(getkth(root,1),0);
- splay(getkth(root,tmp),root);
- tmp=keyv;
- keyv=0;
- pushup(rt(root));
- pushup(root);
-
- // 将去除的那棵左子树插入到末尾
- splay(getkth(root,sz[root]-1),0);
- keyv=tmp;
- pre[keyv]=rt(root);
- pushup(rt(root));
- pushup(root);
-
- // 打上逆转标号
- splay(getkth(root,1),0);
- splay(getkth(root,k1+2),root);
- update_rev(keyv);
- pushup(rt(root));
- pushup(root);
-
- tp= getkth(root,2);
- }
-
- void INSERT(int x){
- splay(tp,0);
- int t=sz[lt(root)]+2;
- splay(getkth(root,t),root);
- newnode(keyv, rt(root),x);
- pushup(rt(root));
- pushup(root);
- }
-
- void DELETE(){
- splay(tp,0);
- int tmp=sz[lt(root)];
- splay(getkth(root,tmp),0);
- splay(getkth(root,tmp+2),root);
-
- s[++tot2]=keyv;
- keyv=0;
- pushup(rt(root));
- pushup(root);
- if (tmp+1==sz[root]) tp=getkth(root,2);
- else tp=getkth(root,tmp+1);
- }
-
- void MOVE(int x){
- splay(tp,0);
- if (x==1){
- int t=sz[lt(root)];
- if (t==1) tp=getkth(root,sz[root]-1);
- else tp=getkth(root,t);
- }else{
- int t=sz[lt(root)]+2;
- if (t==sz[root]) tp=getkth(root,2);
- else tp=getkth(root,t);
- }
- }
-
- int QUERY(){
- splay(tp,0);
- return key[root];
- }
-
- char op[100];
- int x;
- int main()
- {
- int kase=0;
- while (cin>>n>>q>>k1>>k2){
- if (!n && !q && !k1 && !k2) break;
- printf("Case #%d:\n",++kase);
- init();
- tp=getkth(root,2);
- while (q--){
- scanf("%s",op);
- if (op[0]=='a'){
- scanf("%d",&x);
- ADD(x);
- }else if (op[0]=='r'){
- REVERSE();
- }else if (op[0]=='i'){
- scanf("%d",&x);
- INSERT(x);
- }else if (op[0]=='d'){
- DELETE();
- }else if (op[0]=='m'){
- scanf("%d",&x);
- MOVE(x);
- }else{
- printf("%d\n",QUERY());
- }
- }
- }
- return 0;
- }
最后感谢博客: