李嘉豪 @ 2020-12-19 21:19:04
今天晚上想学习一下替罪羊树,顺利的过掉了普通版的数据,跑得也比之前写的其他平衡树快一些,但是在加强版却只有50分,我将alpha分别改为0.8,0.85,0.9分别得到了60分,80分和90分但是按道理系数这么大是不太对劲的。另外我突发奇想在求排名,kth等操作之中都判断一下平衡,但是直接就成了MLE,求哪位巨佬帮忙检查一下板子,代码里面写上了注释,感激不尽。
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 5e6 + 10;
const double alpha = 0.75;
#define rint register int
int read() {
int s = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
s = (s << 1) + (s << 3) + (ch ^ 48);
ch = getchar();
}
return s * f;
}
struct Tree {
int ch[2], val, siz, liv; //ch为两个儿子,val为权值,siz为节点个数(未知是否存在),liv为存在的节点个数
bool exist;
} tree[N];
#define ls(t) tree[t].ch[0] //分别表示左右儿子和中点
#define rs(t) tree[t].ch[1]
#define mid ((l + r) >> 1)
int root;
int Pool[N], tail, tot, cnt[N], top; //Pool是内存池用tail做指针,cnt数组暂存被重构的节点用top做指针
int New(rint val) { //新建一个节点
rint pos = tail ? Pool[tail--] : ++tot;
tree[pos].ch[0] = tree[pos].ch[1] = 0;
tree[pos].val = val;
tree[pos].siz = tree[pos].liv = tree[pos].exist = 1;
return pos;
}
bool isbad(rint t) { //判断是否需要重构
rint now = std::max(tree[ls(t)].siz, tree[rs(t)].siz);
if((double)tree[t].siz * alpha <= now) return true;
return false;
}
void dfs(int t) { //中序遍历,取出重构的序列
if(ls(t)) dfs(ls(t));
if(tree[t].exist) cnt[++top] = t;
else Pool[++tail] = t;
if(rs(t)) dfs(rs(t));
}
void pushup(int t) { //向上修改
tree[t].siz = tree[ls(t)].siz + tree[rs(t)].siz + 1;
tree[t].liv = tree[ls(t)].liv + tree[rs(t)].liv + tree[t].exist;
}
void build(rint &t, rint l, rint r) { //重构树
if(l > r) return t = 0, void();
t = cnt[mid];
build(ls(t), l, mid - 1);
build(rs(t), mid + 1, r);
pushup(t);
}
void rebuild(rint &t) { //重构一条龙
top = 0;
dfs(t);
build(t, 1, top);
}
void Insert(rint &t, rint val) { //插入节点
if(!t) { //到达叶子
t = New(val);
return;
}
tree[t].siz++; tree[t].liv++;
if(isbad(t)) rebuild(t); //判断重构
if(val <= tree[t].val) Insert(ls(t), val);
else Insert(rs(t), val);
pushup(t);
}
void del_kth(rint t, rint rnk) { //删除第k个节点
rint x = tree[ls(t)].liv;
if(tree[t].exist && x + 1 == rnk) {
tree[t].exist = 0;
tree[t].liv--;
return;
}
tree[t].liv--;
if(x >= rnk) del_kth(ls(t), rnk);
else del_kth(rs(t), rnk - x - tree[t].exist);
}
int kth(rint t, rint k) { //求出第k个节点
rint x = tree[ls(t)].liv;
if(tree[t].exist && x + 1 == k) {
return tree[t].val;
}
if(x >= k) return kth(ls(t), k);
else return kth(rs(t), k - x - tree[t].exist);
}
int rank(rint t, rint val) { //求排名
if(!t) return 0;
int x = tree[ls(t)].liv + tree[t].exist;
if(tree[t].val < val) return rank(rs(t), val) + x;
else return rank(ls(t), val);
}
void del(rint val) { //删除节点
rint rnk = rank(root, val) + 1;
del_kth(root, rnk);
}
int pre(rint val) { //求前趋
rint rnk = rank(root, val);
return kth(root, rnk);
}
int nxt(rint val) { //求后继
rint rnk = rank(root, val + 1) + 1;
return kth(root, rnk);
}
int main() {
rint n = read(), m = read(), res = 0, ans = 0;
for(rint i = 1; i <= n; ++i) {
rint w = read();
Insert(root, w);
}
for(rint i = 1; i <= m; ++i) {
rint op = read(), x = read();
x ^= res;
if(op == 1) Insert(root, x);
else if(op == 2) del(x);
else if(op == 3) res = rank(root, x) + 1;
else if(op == 4) res = kth(root, x);
else if(op == 5) res = pre(x);
else if(op == 6) res = nxt(x);
if(op != 1 && op != 2) ans ^= res;
}
printf("%d\n", ans);
return 0;
}
by UltiMadow @ 2020-12-19 21:39:55
@李嘉豪 平衡常数 0.7-0.8 范围内试一试吧(
没必要所有操作都判平衡吧
by UltiMadow @ 2020-12-19 21:40:52
我测下来0.75最优(
by suxxsfe @ 2020-12-19 21:49:03
在insert时应该不是没找到一个坏节点就重构,因为在那之上它的某个祖先也会是坏的,所以应该在insert的路径上记录一个最考上坏节点的指针,然后只重构一次
但这并不会对时间造成太大影响,TLE可能是alpha太大了
并不是所有操作都要判平衡
by suxxsfe @ 2020-12-19 21:49:29
在insert时应该不是每找到一个坏节点就重构
by hzoi_liuchang @ 2020-12-20 06:58:07
%%%%