#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
#define MAX_NODE_NUM 100005
struct Node {
int child[2]; // 左右子节点编号
int parent; // 父节点编号
int val; // 节点数值
int size; // 子树大小
int flag; // 懒标记
} pool[MAX_NODE_NUM];
class SplayTree {
private:
int __root; // 根节点编号
int __pool_pos; // 对象池最后一个分配位置
public:
SplayTree(int node_num) {
__root = 0;
__pool_pos = 1;
memset(pool, 0, sizeof(Node) * (node_num+1));
}
// 翻转懒标记
void rev_flag(int idx) {
pool[idx].flag ^= 1;
}
int get_left_child(int idx) {
return pool[idx].child[0];
}
int get_right_child(int idx) {
return pool[idx].child[1];
}
int get_root() {
return __root;
}
// 搬移函数,将编号为x的节点通过旋转搬移到k号节点的下面
void move(int x, int k) {
__move(x, k);
}
// 插入数值是val的节点
void insert(int val) {
int u = __root, p = 0;
while (u != 0) {
p = u;
u = pool[u].child[val > pool[u].val];
}
u = __pool_pos++;
if (p != 0) {
pool[p].child[val > pool[p].val] = u;
}
pool[u].parent = p;
pool[u].val = val;
__move(u, 0);
}
// 获取第k大的节点编号,返回0表示无解
int get_kth_idx(int k) {
int u = __root;
while (u) {
__push_down(u);
if (pool[pool[u].child[0]].size >= k) {
u = pool[u].child[0];
} else if (pool[pool[u].child[0]].size + 1 == k) {
return u;
} else {
k -= pool[pool[u].child[0]].size + 1;
u = pool[u].child[1];
}
}
return 0;
}
void get_mid_travel_list(int* ans) {
int pos = 0;
__get_mid_travel_list(__root, pos, ans);
}
private:
// 信息上传函数(即根据子节点信息更新序号是idx的节点的信息)
void __push_up(int idx) {
Node& node = pool[idx];
node.size = 1 + pool[node.child[0]].size + pool[node.child[1]].size;
}
/******* 此处 __flag_oper __push_down_child_flag 两个函数的逻辑需要根据不同的问题场景进行修改 **********/
// 懒标记对应的操作,默认什么操作也没有,具体使用时候在此处添加操作
void __flag_oper(int idx) {
swap(pool[idx].child[0], pool[idx].child[1]);
}
// 修改子节点的标记函数,默认就是将标记下传,不同问题可能有不同处理方式,这里把逻辑提出来
void __push_down_child_flag(int idx) {
pool[pool[idx].child[0]].flag ^= 1;
pool[pool[idx].child[1]].flag ^= 1;
}
/******* 此处 __flag_oper __push_down_child_flag 两个函数的逻辑需要根据不同的问题场景进行修改 **********/
// 懒标记下移操作
void __push_down(int idx) {
if (pool[idx].flag) {
__flag_oper(idx);
__push_down_child_flag(idx);
pool[idx].flag = 0;
}
}
// 统一的左旋和右旋函数,将编号为x对应的节点网上一层旋转
void __rotate(int x) {
int y = pool[x].parent, z = pool[y].parent;
int k1 = pool[y].child[1] == x, k2 = pool[z].child[1] == y;
pool[z].child[k2] = x;
pool[x].parent = z;
pool[y].child[k1] = pool[x].child[k1^1];
pool[pool[x].child[k1^1]].parent = y;
pool[x].child[k1^1] = y;
pool[y].parent = x;
__push_up(y);
__push_up(x);
}
// 搬移函数,将编号为x的节点通过旋转搬移到k号节点的下面
// k = 0 表示将x节点搬移到根节点位置
void __move(int x, int k) {
while (pool[x].parent != k) {
int y = pool[x].parent, z = pool[y].parent;
if (z != k) {
if ( (pool[y].child[1] == x) != (pool[z].child[1] == y) ) {
__rotate(x);
} else {
__rotate(y);
}
}
__rotate(x);
}
if (k == 0) {
__root = x;
}
}
void __get_mid_travel_list(int cur, int& pos, int* ans) {
if (cur == 0) {
return;
}
__push_down(cur);
__get_mid_travel_list(pool[cur].child[0], pos, ans);
ans[pos++] = pool[cur].val;
__get_mid_travel_list(pool[cur].child[1], pos, ans);
}
};
int mid_arr[MAX_NODE_NUM];
int main(void) {
//freopen("/Users/grh/Programming/CLionProjects/Test/data.txt", "r", stdin);
int n, m;;
int l, r;
scanf("%d %d", &n, &m);
SplayTree tree(n+2);
for (int i = 0; i <= n+1; i++) {
tree.insert(i);
}
for (int i = 0; i < m; i++) {
scanf("%d %d", &l, &r);
l = tree.get_kth_idx(l);
r = tree.get_kth_idx(r+2);
tree.move(l, 0);
tree.move(r, l);
tree.rev_flag(tree.get_left_child(r));
}
tree.get_mid_travel_list(mid_arr);
for (int i = 1; i <= n; i++) {
printf("%d ", mid_arr[i]);
}
printf("\n");
}
您好,本题要求翻转时,是要找到数列的第k个数字,而get_kth_idx()函数是找到获取第k大的节点编号,这两个为什么等价呢?