[模板]洛谷T3391 文艺平衡树 链表&递归版、无父指针版Splay
指针大法好
无父指针Splay大法好
大佬们的“改变旋转方向”萌新表示不懂,于是就自己乱搞出了下面的搞法。。。
代码如下,萌新写的丑,诸位大佬见谅QwQ~
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<ctime> 5 #include<cstdlib> 6 #include<ctime> 7 8 #include<string> 9 #include<stack> 10 #include<queue> 11 #include<vector> 12 #include<algorithm> 13 #include<map> 14 15 using namespace std; 16 17 struct node{ 18 int key; //结点键值 19 int size; //以本结点为根的子树的结点数量 20 bool lazy; //懒标记,记录对以本结点为根的子树的操作,0表示不旋转,1表示待旋转 21 node *ch[2]; //左右子树指针 22 23 void maintain(){ //维护结点信息(size) 24 size=1; 25 if(ch[0]!=NULL)size+=ch[0]->size; 26 if(ch[1]!=NULL)size+=ch[1]->size; 27 } 28 29 int cmp(int x){ //求在以本结点为根的子树中,排名为x的节点相对于本节点的位置 30 int s=0; 31 if(ch[0]!=NULL)s=ch[0]->size; 32 33 if(x<=s)return 0; //在左子树 34 else if(x==s+1)return -1; //本结点即为所求 35 else return 1; //在右子树 36 } 37 }; 38 39 void pushdown(node *); //懒标记下放 40 void rotate(node* &,bool); //旋转 41 void splay(node* &,int); //按照排名伸展 42 void insert(node *&,int); //没有自带伸展 43 void travel(node *); //遍历 44 45 node *root=NULL; //根节点指针 46 47 int n,m,i; 48 int l,r; 49 int r_x; //待伸展的总排名为r+1的节点在根节点的右子树中的排名 50 51 int main(){ 52 scanf("%d%d",&n,&m); 53 54 for(i=1;i<=n;i++){ 55 insert(root,i); 56 splay(root,i); 57 } //插入并伸展 58 59 for(i=1;i<=m;i++){ 60 scanf("%d%d",&l,&r); 61 62 if(l>1 && r<n){ //一般情况 63 splay(root,l-1); 64 65 r_x=r; 66 if(root->ch[0]!=NULL)r_x-=root->ch[0]->size; //计算r_x 67 68 splay(root->ch[1],r_x); //已将待翻转区间提取至root->ch[1]->ch[0] 69 70 root->ch[1]->ch[0]->lazy^=1; //打标记 71 } 72 73 else if(l==1 && r==n)root->lazy^=1; //若待翻转区间为整个序列,则只需将根节点打上标记即可 74 75 else{ 76 if(l==1){ 77 splay(root,r+1); 78 root->ch[0]->lazy^=1; 79 } //若待翻转区间为[1,r],且r<n,则将结点r+1伸展至根节点,则根节点的左子树即为待翻转区间 80 else{ 81 splay(root,l-1); 82 root->ch[1]->lazy^=1; 83 } //同理 84 } 85 } 86 87 travel(root); //遍历整棵树 88 89 return 0; 90 } 91 92 void pushdown(node *p){ 93 swap(p->ch[0],p->ch[1]); //交换左右子树 94 95 if(p->ch[0]!=NULL)p->ch[0]->lazy^=1; 96 if(p->ch[1]!=NULL)p->ch[1]->lazy^=1; //下放到左右子树 97 98 p->lazy=0; //清空本节点的懒标记 99 } 100 101 void rotate(node* &p,bool f){ 102 if(p->lazy)pushdown(p); //下放顺序:自上而下 103 104 node *t=p->ch[f^1]; 105 106 if(t->lazy)pushdown(t); 107 108 p->ch[f^1]=t->ch[f]; 109 t->ch[f]=p; 110 111 p->maintain(); //维护顺序:自底向上 112 t->maintain(); 113 114 p=t; 115 } 116 117 void splay(node* &p,int x){ 118 if(p->lazy)pushdown(p); //由于要操作p的子树,故需下放,下面同理 119 120 int d1=p->cmp(x); //d1:待伸展节点相对于p的位置 121 122 if(d1==-1 || p->ch[d1]==NULL)return; //若当前节点即为待伸展节点,或d1指向的子树为空,则直接返回 123 124 if(p->ch[d1]->lazy)pushdown(p->ch[d1]); 125 126 int x2; 127 if(d1==0)x2=x; 128 else{ 129 if(p->ch[0]==NULL)x2=x-1; 130 else x2=x-p->ch[0]->size-1; 131 } //x2:待伸展节点在d1指向的子树中的排名 132 133 int d2=p->ch[d1]->cmp(x2); //d2:待伸展节点相对于d1指向的节点的位置 134 135 if(d2==-1 || p->ch[d1]->ch[d2]==NULL){ 136 rotate(p,d1^1); 137 return; 138 } //若d1指向的节点即为待伸展节点,或d2指向的子树为空,则直接将d1指向的节点上旋,然后返回即可 139 else{ 140 int x3; //在此处,由于splay函数在开始执行时会pushdown,故不需在此处pushdown 141 if(d2==0)x3=x2; 142 else{ 143 if(p->ch[d1]->ch[0]==NULL)x3=x2-1; 144 else x3=x2-p->ch[d1]->ch[0]->size-1; 145 } //x3:待伸展节点在d2指向的子树中的排名 146 147 splay(p->ch[d1]->ch[d2],x3); //将待伸展节点递归伸展至d2指向的点 148 149 if(d1==d2){ //一字型旋转 150 rotate(p,d1^1); 151 rotate(p,d2^1); 152 } 153 else{ //之字形旋转 154 rotate(p->ch[d1],d1); //d2^1==d1 155 rotate(p,d2); //d1^1==d2 156 } 157 } 158 } 159 160 void insert(node* &p,int x){ 161 if(p==NULL){ 162 p=(node *)malloc(sizeof(node)); 163 p->key=x; 164 p->size=1; 165 p->lazy=0; 166 p->ch[0]=p->ch[1]=NULL; 167 return; 168 } //新建节点 169 else{ 170 if(p->lazy)pushdown(p); //由于要操作p的子树,故需下放 171 insert(p->ch[1],x); //由于按左右顺序排名,故需插入至最右端 172 p->size++; //维护本节点信息 173 } 174 } 175 176 void travel(node *p){ 177 if(p->lazy)pushdown(p); //先进行下放,于是可以得到正确的顺序,然后遍历即可 178 179 if(p->ch[0]!=NULL)travel(p->ch[0]); //递归遍历左子树 180 181 printf("%d ",p->key); //遍历本节点 182 183 if(p->ch[1]!=NULL)travel(p->ch[1]); //递归遍历右子树 184 }