社区讨论

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 条回复,欢迎继续交流。

正在加载回复...