JZYZOJ1998 [bzoj3223] 文艺平衡树 splay 平衡树
http://172.20.6.3/Problem_Show.asp?id=1998
平衡树区间翻转的板子,重新写一遍,给自己码一个板子。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<cstdlib> 7 using namespace std; 8 const int maxn=100100; 9 int c[maxn][2],fa[maxn],siz[maxn],rev[maxn]; 10 int n,m,rt; 11 inline int read(){ 12 int x=0,f=1;char ch=getchar(); 13 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 14 while(ch>='0'&&ch<='9'){x*=10;x+=ch-'0';ch=getchar();} 15 return x*f; 16 } 17 inline void pushup(int x){//向上总结siz 18 siz[x]=siz[c[x][0]]+siz[c[x][1]]+1; 19 } 20 inline void pushdown(int x){//向下传递rev标记 21 if(rev[x]){ 22 swap(c[x][0],c[x][1]); 23 rev[c[x][0]]^=1;rev[c[x][1]]^=1; 24 rev[x]=0; 25 } 26 } 27 void build(int l,int r,int pa){//不完全二分建树。。 28 if(l>r)return; 29 if(l==r){ 30 fa[l]=pa;siz[l]=1; 31 if(l<pa)c[pa][0]=l; 32 else c[pa][1]=l; 33 return; 34 } 35 int mid=(l+r)/2; 36 build(l,mid-1,mid);build(mid+1,r,mid); 37 if(mid<pa)c[pa][0]=mid; 38 else c[pa][1]=mid; 39 fa[mid]=pa; 40 pushup(mid); 41 } 42 int find(int x,int rank){//二分查找排名为rank的数 43 //因为建树的时候已经默认查找的数在数组中的序号就是它本身 44 //操作不会改变一个节点的序号,只会改变点和点的联系,从而改变排列顺序 45 pushdown(x); 46 //find中的pushdown相当于为后面的splay“开路” 47 int l=c[x][0],r=c[x][1]; 48 if(siz[l]+1==rank)return x; 49 else if(siz[l]>=rank)return find(l,rank); 50 else return find(r,rank-siz[l]-1); 51 } 52 void rotate(int x,int &k){//向上旋转 53 int pa=fa[x];int gpa=fa[pa],l,r; 54 if(c[pa][0]==x) l=0; 55 else l=1; r=l^1;//上旋前x在pa的l方向 56 if(pa==k) k=x; 57 else{ 58 //如果x旋转不到k,需要关注上旋后和gpa的儿子关系 59 //观察后面的调用,旋转到k时gpa的儿子关系相当于直接被继承, 60 if(c[gpa][0]==pa)c[gpa][0]=x; 61 else c[gpa][1]=x; 62 } 63 fa[x]=gpa;fa[pa]=x;fa[c[x][r]]=pa; 64 c[pa][l]=c[x][r];c[x][r]=pa; 65 pushup(x);pushup(pa); 66 67 } 68 void splay(int x,int &k){ 69 while(x!=k){ 70 int pa=fa[x];int gpa=fa[pa]; 71 if(pa!=k){//这里的异或处理了顺位和逆位两种情况 72 /*顺位先将pa上旋至k,逆位直接将x向上旋, 73 避免树失衡。*/ 74 if((c[pa][0]==x)^(c[gpa][0]==pa))rotate(x,k); 75 else rotate(pa,k); 76 }rotate(x,k); 77 } 78 } 79 void rever(int l,int r){ 80 int x=find(rt,l),y=find(rt,r+2);//找到两个数的位置 81 splay(x,rt);splay(y,c[x][1]);//将两个数调为根和其rc 82 //此时,两个数之间的数都在根的rc的lc上 83 /* 我们开始开始的建树从1到n+2就是为了将1和n+2作为边界 84 事实上进行操作的是2到n+1这n个数。我们最后取的时候要减1。 85 所以,翻转l+1到r+1即可。 86 */ 87 rev[c[y][0]]^=1; 88 } 89 int main(){ 90 n=read();m=read(); 91 int x,y; 92 build(1,n+2,0);rt=(n+3)/2; 93 for(int i=1;i<=m;i++){ 94 x=read();y=read(); 95 rever(x,y); 96 } 97 for(int i=2;i<=n+1;i++){ 98 printf("%d ",find(rt,i)-1); 99 } 100 return 0; 101 }