Splay TLE37pts求调,悬关
查看原帖
Splay TLE37pts求调,悬关
716260
SegmentTree_楼主2024/6/21 00:43
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5+5;
/*
二叉搜索树,又称二叉搜索树,
是一种优化查询数字的复杂度的
数据结构,他的结点严格满足左

结点小于根节点,右结点大于根节点
son[0]左儿子 son[1]右儿子
val 值
cnt 有多少个这个值
siz 所在子树元素个数
fa 父节点
*/
struct node{
	int son[2];
	int val;
	int cnt;
	int siz;
	int fa;
}tr[2000005];
int root, tot;
// root根节点 tot节点数量
bool Dir(int x){
	return tr[tr[x].fa].son[1] == x;
}
void pushup(int x){
	tr[x].siz = tr[tr[x].son[0]].siz + tr[tr[x].son[1]].siz + tr[x].cnt;
}
void rorate(int x){
	int y = tr[x].fa, z = tr[y].fa, k = Dir(x), w = tr[x].son[k ^ 1];
	// 原来爷爷节点指向父节点的连边现在指向了当前节点
	tr[z].son[Dir(y)] = x;tr[x].fa = z;
	/*
	现在当前节点为原来父节点的父节点,
	当k=0,val[x]<val[y],val[y]>val[x],y节点就为x的右子节点
	k=1同样
	*/
	tr[x].son[k ^ 1] = y;tr[y].fa = x;
	// 还没懂
	tr[y].son[k] = w;tr[w].fa = y;
	pushup(x);pushup(y);
}
void splay(int x, int goal = 0){
	// x要成为goal的子节点,goal=0时x要成为根节点
	// 如果祖宗三代在同一个方向,要先转父,再转当前
	while (tr[x].fa != goal){
		int y = tr[x].fa, z = tr[y].fa;
		if (z != goal){
			if (Dir(x) == Dir(y)) rorate(y);
			else rorate(x);
		}
		rorate(x);
	}
	if (!goal) root = x;
}
void find(int x){
	/*
	将x这个数字伸展到根节点
	如果没有x,根就为x的前驱或后继
	*/
	if (!root) return;
	int cur = root;
	// 找val为x的节点
	while (tr[cur].son[x > tr[cur].val] && tr[cur].val != x){
		cur = tr[cur].son[x > tr[cur].val];
	}
	splay(cur);
}
void insert(int x){
	/*
	和find差不多,先找x,找到就++cnt,找不到就新建节点
	*/
	int cur = root, p = 0;
	while (cur && tr[cur].val != x){
		p = cur;
		cur = tr[cur].son[x > tr[cur].val];
	}
	if (cur) ++tr[cur].cnt;
	else{
		cur = ++tot;
		if (p) tr[p].son[x > tr[p].val] = cur;
		tr[cur].son[0] = tr[cur].son[1] = 0;
		tr[cur].val = x;
		tr[cur].fa = p;
		tr[cur].siz = tr[cur].cnt = 1;
	}
	splay(cur);
}
int getpre(int x){
	/*
	x的前驱就是x最右边的左儿子
	*/
	find(x);
	if (tr[root].val < x) return root;
	int cur = tr[root].son[0];
	while (tr[cur].son[1]) cur = tr[cur].son[1];
	splay(cur);
	return cur;
}
int getsuf(int x){
	/*
	最左的右子节点
	*/
	find(x);
	if (tr[root].val > x) return root;
	int cur = tr[root].son[1];
	while (tr[cur].son[0]) tr[cur].son[0];
	splay(cur);
	return cur;
}
void del(int x){
	/*
	x的前驱和后继之间的数只有x
	将前驱伸展至根,后继伸展为前驱的子节点,就会变成
	son[后继][0]=x
	*/
	int p = getpre(x), s = getsuf(x);
	splay(p), splay(s, p);
	int tdel = tr[s].son[0];
	if (tr[tdel].cnt > 1){
		--tr[tdel].cnt;
		splay(tdel);
	}
	else tr[s].son[0] = 0;
	pushup(s), pushup(root);
}
int getrank(int x){
	/*
	find(x),如果有x这个点,rank就是左子树大小,
	如果没有并且根是前驱,rank就是左子树大小+根的cnt
	*/
	find(x);
	if (tr[root].val < x) return tr[tr[root].son[0]].siz + tr[root].cnt;
	else return tr[tr[root].son[0]].siz;
}
int getkth(int k){
	/*
	和权值线段树kth差不多
	如果左节点大小>=k就往左走,
	否则往右走,k-=左子树大小
	如果没有节点会死循环,所以一开始要插入极大值和极小值
	*/
	int cur = root;
	while (1){
		if (tr[cur].son[0] && k <= tr[tr[cur].son[0]].siz){
			cur = tr[cur].son[0];
		}
		else{
			int tmp = (tr[cur].son[0] ? tr[tr[cur].son[0]].siz : 0) + tr[cur].cnt;
			if (k <= tmp){
				splay(cur);
				return cur;
			}
			k -= tmp;
			cur = tr[cur].son[1];
		}
	}
}
int main(){
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int n;
	cin >> n;
	insert(-INT_MAX);
	insert(INT_MAX);
	while (n--){
		int op, x;
		cin >> op >> x;
		if (op == 1){
			insert(x);
		}
		else if (op == 2){
			del(x);
		}
		else if (op == 3){
			cout << getrank(x) << '\n';
		}
		else if (op == 4){
			cout << tr[getkth(x + 1)].val << '\n';
		}
		else if (op == 5){
			cout << tr[getpre(x)].val << '\n';
		}
		else{
			cout << tr[getsuf(x)].val << '\n';
		}
	}
	return 0;
} 
2024/6/21 00:43
加载中...