POJ - 3481 Double Queue 【Splay 模板题】
传送门
最基本的Splay模板题,没有任何花里胡哨的操作,可以用于加深理解模板中每个函数。
Splay学习博客:
https://baijiahao.baidu.com/s?id=1613228134219334653&wfr=spider&for=pc
https://www.cnblogs.com/cjyyb/p/7499020.html ///推荐当模板
代码:(有详细注释+样例)
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6 + 5;
int father[maxn], child[maxn][2], size[maxn];
int cnt[maxn], root, times;
int value[maxn], sign[maxn];
bool check(int x) { ///查询x是father[x]的左儿子还是右儿子
return child[father[x]][1] == x;
}
void pushup(int x) {
size[x] = size[child[x][0]] + size[child[x][1]] + cnt[x];
}
void rotate(int x) { ///旋转操作,将x和father[x]换位置
int y = father[x], z = father[y], k = check(x), w = child[x][k ^ 1];
child[y][k] = w, father[w] = y;
child[z][check(y)] = x, father[x] = z;
child[x][k ^ 1] = y, father[y] = x;
pushup(y), pushup(x);
}
void Splay(int x, int goal) { ///将x节点旋转到goal的下方
while (father[x] != goal) {
int y = father[x], z = father[y];
if (z != goal) { ///存在爷爷节点
if (check(x) == check(y)) ///如果在链的同一侧(为了保证树的平衡)
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
root = x;
}
void Insert(int x, int flag) { ///插入操作
int temp = root, fa = 0;
while (temp && value[temp] != x) {
fa = temp;
temp = child[temp][x > value[temp]];
}
if (temp)
cnt[temp]++;
else {
temp = ++times;
if (fa) ///如果存在父亲(不存在父亲就是一棵空树,u就是当前的根)
child[fa][x > value[fa]] = temp;
child[temp][0] = child[temp][1] = 0;
sign[temp] = flag;
father[temp] = fa, value[temp] = x;
cnt[temp] = size[temp] = 1;
}
Splay(temp, 0);
}
int find_k(int k) { ///查询中序遍历的第k个的节点
int temp = root;
while (1) {
//pushdown(temp); ///标记下传
if (child[temp][0] && k <= size[child[temp][0]]) {
temp = child[temp][0];
} else if (k > size[child[temp][0]] + cnt[temp]) {
k -= (size[child[temp][0]] + cnt[temp]); ///先计算数量后向下递归
temp = child[temp][1];
} else
return temp;
}
}
void find(int x) { ///如果存在最大的小于等于的数所在的节点splay到根,不存在就是第一个大于x的节点
int temp = root;
while (child[temp][x > value[temp]] && value[temp] != x)
temp = child[temp][x > value[temp]];
Splay(temp, 0);
}
int Next(int x, int flag) { ///flag=0,查找前驱 flag=1查找后继
find(x);
int temp = root;
if (value[temp] < x && !flag)
return temp;
if (value[temp] > x && flag)
return temp;
temp = child[temp][flag];
while (child[temp][flag ^ 1])
temp = child[temp][flag ^ 1];
return temp;
}
void Delete(int x) { ///删除权值为x的节点
int pre = Next(x, 0);
int last = Next(x, 1);
Splay(pre, 0), Splay(last, pre);
int temp = child[last][0];
if (cnt[temp] > 1) { ///存在多个则cnt计数-1
cnt[temp]--;
Splay(temp, 0);
} else ///删除该节点
child[last][0] = 0;
}
int main() {
int op, k, p, num = 2; ///记录数中的节点个数
Insert(-1, 0), Insert(INF, 0); ///方便删除节点
while(scanf("%d", &op) != EOF, op) {
if(op == 1) {
scanf("%d %d", &k, &p);
Insert(p, k);
num++;
} else if(op == 2) {
if(num == 2) {
printf("0\n");
continue;
}
int x = find_k(num - 1);
printf("%d\n", sign[x]);
Delete(value[x]);
num--;
} else {
if(num == 2) {
printf("0\n");
continue;
}
int x = find_k(2);
printf("%d\n", sign[x]);
Delete(value[x]);
num--;
}
}
return 0;
}
/*
1 1 1
1 2 2
1 3 3
1 4 4
1 5 5
1 6 6
1 7 7
1 8 8
1 9 9
2
2
2
2
2
2
2
2
2
0
Output:
9
8
7
6
5
4
3
2
1
*/