社区讨论
TLE求助!!码风究极良好,附有注释(平衡树Spaly做法)n玄关
P3391【模板】文艺平衡树参与者 1已保存回复 0
讨论操作
快速查看讨论及其快照的属性,并进行相关操作。
- 当前回复
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mib634d0
- 此快照首次捕获于
- 2025/11/23 11:37 3 个月前
- 此快照最后确认于
- 2025/11/23 13:13 3 个月前
既然点进来了,就看看吧,我喊我全班同学关注你
CPP#include <cstdio>
#include <algorithm>
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int MAXSZ = 1 << 20;
char ch, buf[MAXSZ], *p1, *p2;
#define ge() (p1 == p2 && (p2 = buf + fread(p1 = buf, 1, MAXSZ, stdin), p1 == p2) ? EOF : *p1++)
template <typename T_T>
inline void read(T_T &x) {
x = 0;
while (ch < '0' || '9' < ch) ch = ge();
while ('0' <= ch && ch <= '9') {
x = x * 10 + (ch ^ 48);
ch = ge();
}
}
template <typename T_T>
inline void write(T_T x) {
if (x > 9) write(x / 10);
putchar(x % 10 | 48);
}
//-----------------------前面都不需要管,是快读快出
const int N = 1e5 + 5;
int tag_l, tag_r, n, T; // 操作区间,数字个数,操作次数
struct Splay {
Splay *son[2], *fa; // 两个儿子,一个父亲
int val, sz; // 这个节点的值,子树大小
bool tag; // 懒标记(旋转)
inline bool dir() { return fa->son[1] == this; } // 判断方向
inline void push_up() { sz = son[0]->sz + son[1]->sz + 1; } // 上传大小
inline void push_down() { // 下传懒标记,相信里面的内容大家的看得懂
if (tag == false) return;
std::swap(son[0], son[1]);
if (son[0]->sz != 0) son[0]->tag ^= 1;
if (son[1]->sz != 0) son[1]->tag ^= 1;
tag = false;
}
} SP[N], *Psplay = SP, *null = Psplay, *root; // 分别表示:预开的空间,节点指针"id",空节点,根
Splay *target; // Splay里需要的目标
void Print() { // 调试语句输出树
Splay *u = SP + 1;
for (int i = 1; i <= n; i++)
printf("i:%d Father:%d lson:%d rson:%d Tag:%d\n", i, u -> fa -> val, u -> son[0] -> val, u -> son[1] -> val, u -> tag), ++ u;
}
void rotate(Splay *u) { // 标准双旋操作,没有任何特殊的地方
Splay *fa = u->fa, *gr = fa->fa;
fa->push_down(), u->push_down();
bool d = u->dir();
Splay *v = u->son[!d];
u->fa = gr;
if (gr != target)
gr->son[fa->dir()] = u;
fa->fa = u, u->son[!d] = fa;
v->fa = fa, fa->son[d] = v;
fa->push_up(), u->push_up();
}
Splay *splay(Splay *u, Splay *_target = null) { // Splay操作,实现了把点u旋转到target的操作
target = _target->fa;
for (Splay *f; u->fa != target; rotate(u)) // Splay的巧妙实现方法,但思路一样不变
if ((f = u->fa)->fa != target)
rotate(u->dir() == f->dir() ? f : u);
return u;
}
Splay *find_val(int k) { // 寻找第 k 个数字是哪一个
Splay *u = root;
while (k != u -> son[0] -> sz + 1) {
u -> push_down();
if (k > u -> son[0] -> sz + 1) k -= u -> son[0] -> sz + 1, u = u->son[1];
else u = u -> son[0];
}
u -> push_down();
return u;
}
void first(Splay *u) { // 中序输出,输出答案
if (u == null) return;
u -> push_down();
first(u -> son[0]);
if (u -> val >= 1 && u -> val <= n) // 因为有两个哨兵,所以需要判断值是否满足[1,n]
write(u -> val), putchar(' ');
first(u -> son[1]);
}
void reverse() { // tag_l到tag_r进行旋转操作
Splay *u = find_val(tag_l), *v = find_val(tag_r + 2); // 因为有哨兵,所以u、v对应的应该是点tag_l-1和tag_r+1
root = u = splay(u); // 旋转到根
v = splay(v, u); // 旋转到u的右儿子
Splay *sub = v -> son[0];
if (sub != null) // 更新懒标记
sub -> tag ^= 1;
}
void build(Splay *&u, Splay *fa, int l, int r) { // 建树
if (l > r) { // 空区间、空节点
u = null;
return;
}
int mid = (l + r) >> 1;
* (u = ++ Psplay) = {{null, null}, fa, mid, 1, false}; // 正常取出节点
build(u -> son[0], u, l, mid - 1); // 正常向两边扩展
build(u -> son[1], u, mid + 1, r);
u -> push_up(); // 正常上传大小
}
int main() {
// freopen("Ryan.in", "r", stdin);
// freopen("Ryan.out", "w", stdout);
* null = {{null, null}, null, 0, 0, false}; // 定义空节点
read(n), read(T); // 读入建树
build(root, null, 0, n + 1);
while (T --) {
read(tag_l), read(tag_r); // 操作+1
reverse();
// first(root), putchar('\n');
// Print(), putchar('\n');
}
first(root), putchar('\n');
return 0;
}
回复
共 0 条回复,欢迎继续交流。
正在加载回复...