上午在打zroi的比赛,然而菜得只会T1,虽然是50分的O(n2)写法,大概就是维护一个前缀和,后缀max以及max值,这三个都是用n2的时间进行更新的,最后求值的时候也是n2的,好像线段树维护一下就可以AC了,不是很清楚,出了成绩再说吧
下午打了打cp的比赛,发现自己什么都不会,弃疗之后手玩提答,AC之后就没时间了
晚上继续学splay,终于敲出来了splay版的普通平衡树,过程中模板敲错n次,真实彩笔
这里给出详细注释的板子:
1 //普通平衡树 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<cstdlib> 6 #include<cctype> 7 #include<algorithm> 8 using namespace std; 9 int root=0,len=0,n; 10 struct node{ 11 int v,size,weight,son[2],f; 12 }tree[100010]; 13 14 inline void maintain(int pos) 15 {tree[pos].size=tree[tree[pos].son[0]].size+tree[tree[pos].son[1]].size+tree[pos].weight;}//重新计算weight 16 17 inline int relation(int pos) 18 {return (pos==tree[tree[pos].f].son[0])? 0:1;}//判断当前节点与父节点之间的关系 19 20 inline void ins(int x,int y,int z)//在x下面插入y,flag值为z(=1为右儿子,=2为左儿子) 21 {tree[x].son[z]=y;tree[y].f=x;} 22 23 inline void rotate(int pos){//旋转 24 int f=tree[pos].f,flag=relation(pos); 25 ins(tree[f].f,pos,relation(f));//将pos作为f的父亲的儿子插入,与f原位置相同 26 ins(f,tree[pos].son[flag^1],flag);//将原pos的flag^1儿子作为pos的儿子,与pos原位置相同 27 ins(pos,f,flag^1);//将f作为pos的儿子插入,flag值为flag^1 28 maintain(f); maintain(pos);//重新计算f和pos的size(旋转过程中只有pos和f的size发生了变化) 29 if (!tree[pos].f) root=pos;//判断pos是否为根节点 30 } 31 32 void splay(int pos,int target=0){//pos为要旋转到根的节点,target表示在哪个节点的子树中进行splay操作 33 while (tree[pos].f!=target){ 34 if (tree[tree[pos].f].f==target) rotate(pos);//不需要双旋时 35 else if (relation(pos)==relation(tree[pos].f)){//成直链时先旋父节点再选当前节点 36 rotate(tree[pos].f); rotate(pos); 37 } 38 else{//不成直链时对当前节点旋转两次 39 rotate(pos); rotate(pos); 40 } 41 } 42 } 43 44 int pre(int v){//查询v的前驱并splay到根 45 int ans,pos=root;//ans为要求的前驱,pos为当前位置,当pos不存在时,查找结束 46 while (pos){ 47 if (tree[pos].v>=v) pos=tree[pos].son[0];//当前节点不小于v,在左子树中进行查找 48 else{ 49 ans=pos; pos=tree[pos].son[1];//当前位置为新的最优解,对ans进行更新,同时在右子树中进行查找 50 } 51 } 52 splay(ans);//将目的节点旋转到根 53 return root; 54 } 55 56 int succ(int v){ 57 int ans,pos=root; 58 while (pos){ 59 if (tree[pos].v<=v) pos=tree[pos].son[1]; 60 else{ 61 ans=pos; pos=tree[pos].son[0]; 62 } 63 } 64 splay(ans); 65 return root; 66 } 67 68 69 int find(int v){//查找值为v的节点 70 int pos=root; 71 while (pos&&tree[pos].v!=v){ 72 if (v<tree[pos].v) pos=tree[pos].son[0]; 73 else pos=tree[pos].son[1]; 74 } 75 if (pos) splay(pos);//如果这个节点存在,对其进行splay操作 76 return root; 77 } 78 79 void del(int v){//删除一个值为v的节点 80 int pos=find(v); 81 if (tree[pos].weight>1){//值为v的节点不止一个,只需减少节点的weight和size即可(节点已旋转到根) 82 tree[pos].size--;tree[pos].weight--;return; 83 } 84 int s,p;//s为后继,p为前驱 85 s=succ(v);p=pre(v); 86 splay(s,p);//p为根节点,将s splay到p的右儿子,此时pos在s的左儿子处,只需要修改pos,s,p三点即可 87 tree[s].son[0]=0; tree[s].size--; tree[p].size--; 88 } 89 90 int insert(int v){//插入值为v的节点 91 int pos=root,last=0; 92 while (pos&&tree[pos].v!=v){//查找过程更新路径上的size 93 tree[pos].size++; 94 last=pos; 95 if (v<tree[pos].v) pos=tree[pos].son[0]; 96 else pos=tree[pos].son[1]; 97 } 98 if (pos){//该节点已存在 99 tree[pos].size++; tree[pos].weight++; 100 } 101 else{//该节点不存在,新建节点 102 tree[pos=++len].v=v; tree[pos].son[0]=tree[pos].son[1]=0; tree[pos].size=tree[pos].weight=1; 103 tree[pos].f=last; tree[last].son[(v<tree[last].v)?0:1]=pos; 104 } 105 splay(pos);//将该节点splay到根 106 return root; 107 } 108 109 int rank(int v){//查找v的排名 110 int pos=find(v); 111 if (!pos){//该节点不存在,新建节点,获取排名后删去 112 pos=insert(v); 113 int ans=tree[tree[pos].son[0]].size;//当前节点已旋转到根,其排名即为左子树的size值 114 del(v); 115 return ans; 116 } 117 return tree[tree[pos].son[0]].size;//当前节点已旋转到根,其排名即为左子树的size值 118 } 119 120 int kth(int k){//查找排名为k的值,方式与find相同 121 int pos=root,sz; 122 sz=tree[tree[pos].son[0]].size; 123 while (k<sz||k>=sz+tree[pos].weight){ 124 if (k<sz) pos=tree[pos].son[0]; 125 else{ 126 k-=sz+tree[pos].weight; pos=tree[pos].son[1]; 127 } 128 sz=tree[tree[pos].son[0]].size; 129 } 130 splay(pos); 131 return tree[pos].v; 132 } 133 134 char buf[1<<15],*fs,*ft; 135 inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;} 136 137 int read(){ 138 int x=0,y=1;char ch=getc(); 139 while (!isdigit(ch)){if (ch==‘-‘)y=-1;ch=getc();} 140 while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-‘0‘;ch=getc();} 141 return x*y; 142 } 143 144 void put(int x){ 145 if (x==0){putchar(‘0‘);putchar(‘\n‘);return;} 146 if (x<0){putchar(‘-‘);x=-x;} 147 char ch[15];int top=0; 148 while (x){ch[++top]=x%10+‘0‘;x/=10;} 149 while (top) putchar(ch[top--]); 150 putchar(‘\n‘); 151 } 152 153 int main(){ 154 n=read(); 155 //插入max和min节点,防止出现删除过程中前驱后继不存在的情况 156 insert(0x7fffffff); 157 root=1; 158 insert(0x80000000); 159 while (n--){ 160 int opt,x; 161 opt=read();x=read(); 162 if (opt==1) insert(x); 163 else if (opt==2) del(x); 164 else if (opt==3) put(rank(x));//因为左子树中存在min节点,因此只输出左子树的size值即可 165 else if (opt==4) put(kth(x)); 166 else if (opt==5) put(tree[pre(x)].v); 167 else if (opt==6) put(tree[succ(x)].v); 168 } 169 return 0; 170 }