用户工具

站点工具


2020-2021:teams:legal_string:lgwza:splay

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
2020-2021:teams:legal_string:lgwza:splay [2020/08/22 22:05]
lgwza
2020-2021:teams:legal_string:lgwza:splay [2020/08/22 22:11] (当前版本)
lgwza [删除操作]
行 209: 行 209:
   * 如果 $x$ 和 $y$ 其中之一或两者都为空树,直接返回不为空的那一棵树的根结点或空树。   * 如果 $x$ 和 $y$ 其中之一或两者都为空树,直接返回不为空的那一棵树的根结点或空树。
   * 否则将 $x$ 树中的最大值 $\text{Splay}$ 到根,然后把它的右子树设置为 $y$ 并更新结点的信息,然后返回这个结点。   * 否则将 $x$ 树中的最大值 $\text{Splay}$ 到根,然后把它的右子树设置为 $y$ 并更新结点的信息,然后返回这个结点。
 +
 +==== 删除操作 ====
 +
 +删除操作也是一个比较复杂的操作,具体步骤如下:
 +
 +首先将 $x$ 旋转到根的位置。
 +
 +  * 如果 $cnt[x]>​1$(有不止一个 $x$),那么将 $cnt[x]$ 减 $1$ 并退出。
 +  * 否则,合并它的左右两棵子树即可。
 +
 +<code cpp>
 +void del(int k) {
 +  rk(k);
 +  if (cnt[rt] > 1) {
 +    cnt[rt]--;
 +    maintain(rt);​
 +    return;
 +  }
 +  if (!ch[rt][0] && !ch[rt][1]) {
 +    clear(rt);
 +    rt = 0;
 +    return;
 +  }
 +  if (!ch[rt][0]) {
 +    int cnr = rt;
 +    rt = ch[rt][1];
 +    fa[rt] = 0;
 +    clear(cnr);
 +    return;
 +  }
 +  if (!ch[rt][1]) {
 +    int cnr = rt;
 +    rt = ch[rt][0];
 +    fa[rt] = 0;
 +    clear(cnr);
 +    return;
 +  }
 +  int cnr = rt, x = pre();
 +  splay(x);
 +  fa[ch[cnr][1]] = x;
 +  ch[x][1] = ch[cnr][1];
 +  clear(cnr);
 +  maintain(rt);​
 +}
 +
 +</​code>​
 +
 +===== 完整代码 =====
 +
 +<​hidden>​
 +<code cpp>
 +#include <​cstdio>​
 +const int N = 100005;
 +int rt, tot, fa[N], ch[N][2], val[N], cnt[N], sz[N];
 +struct Splay {
 +  void maintain(int x) { sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + cnt[x]; }
 +  bool get(int x) { return x == ch[fa[x]][1];​ }
 +  void clear(int x) {
 +    ch[x][0] = ch[x][1] = fa[x] = val[x] = sz[x] = cnt[x] = 0;
 +  }
 +  void rotate(int x) {
 +    int y = fa[x], z = fa[y], chk = get(x);
 +    ch[y][chk] = ch[x][chk ^ 1];
 +    fa[ch[x][chk ^ 1]] = y;
 +    ch[x][chk ^ 1] = y;
 +    fa[y] = x;
 +    fa[x] = z;
 +    if (z) ch[z][y == ch[z][1]] = x;
 +    maintain(x);​
 +    maintain(y);​
 +  }
 +  void splay(int x) {
 +    for (int f = fa[x]; f = fa[x], f; rotate(x))
 +      if (fa[f]) rotate(get(x) == get(f) ? f : x);
 +    rt = x;
 +  }
 +  void ins(int k) {
 +    if (!rt) {
 +      val[++tot] = k;
 +      cnt[tot]++;
 +      rt = tot;
 +      maintain(rt);​
 +      return;
 +    }
 +    int cnr = rt, f = 0;
 +    while (1) {
 +      if (val[cnr] == k) {
 +        cnt[cnr]++;
 +        maintain(cnr);​
 +        maintain(f);​
 +        splay(cnr);
 +        break;
 +      }
 +      f = cnr;
 +      cnr = ch[cnr][val[cnr] < k];
 +      if (!cnr) {
 +        val[++tot] = k;
 +        cnt[tot]++;
 +        fa[tot] = f;
 +        ch[f][val[f] < k] = tot;
 +        maintain(tot);​
 +        maintain(f);​
 +        splay(tot);
 +        break;
 +      }
 +    }
 +  }
 +  int rk(int k) {
 +    int res = 0, cnr = rt;
 +    while (1) {
 +      if (k < val[cnr]) {
 +        cnr = ch[cnr][0];
 +      } else {
 +        res += sz[ch[cnr][0]];​
 +        if (k == val[cnr]) {
 +          splay(cnr);
 +          return res + 1;
 +        }
 +        res += cnt[cnr];
 +        cnr = ch[cnr][1];
 +      }
 +    }
 +  }
 +  int kth(int k) {
 +    int cnr = rt;
 +    while (1) {
 +      if (ch[cnr][0] && k <= sz[ch[cnr][0]]) {
 +        cnr = ch[cnr][0];
 +      } else {
 +        k -= cnt[cnr] + sz[ch[cnr][0]];​
 +        if (k <= 0) {
 +          splay(cnr);
 +          return val[cnr];
 +        }
 +        cnr = ch[cnr][1];
 +      }
 +    }
 +  }
 +  int pre() {
 +    int cnr = ch[rt][0];
 +    while (ch[cnr][1]) cnr = ch[cnr][1];
 +    splay(cnr);
 +    return cnr;
 +  }
 +  int nxt() {
 +    int cnr = ch[rt][1];
 +    while (ch[cnr][0]) cnr = ch[cnr][0];
 +    splay(cnr);
 +    return cnr;
 +  }
 +  void del(int k) {
 +    rk(k);
 +    if (cnt[rt] > 1) {
 +      cnt[rt]--;
 +      maintain(rt);​
 +      return;
 +    }
 +    if (!ch[rt][0] && !ch[rt][1]) {
 +      clear(rt);
 +      rt = 0;
 +      return;
 +    }
 +    if (!ch[rt][0]) {
 +      int cnr = rt;
 +      rt = ch[rt][1];
 +      fa[rt] = 0;
 +      clear(cnr);
 +      return;
 +    }
 +    if (!ch[rt][1]) {
 +      int cnr = rt;
 +      rt = ch[rt][0];
 +      fa[rt] = 0;
 +      clear(cnr);
 +      return;
 +    }
 +    int cnr = rt;
 +    int x = pre();
 +    splay(x);
 +    fa[ch[cnr][1]] = x;
 +    ch[x][1] = ch[cnr][1];
 +    clear(cnr);
 +    maintain(rt);​
 +  }
 +} tree;
 +
 +int main() {
 +  int n, opt, x;
 +  for (scanf("​%d",​ &n); n; --n) {
 +    scanf("​%d%d",​ &opt, &x);
 +    if (opt == 1)
 +      tree.ins(x);​
 +    else if (opt == 2)
 +      tree.del(x);​
 +    else if (opt == 3)
 +      printf("​%d\n",​ tree.rk(x));​
 +    else if (opt == 4)
 +      prin
 +</​code>​
 +</​hidden>​
 +
 +===== 例题 =====
 +
 +以下题目都是裸的 $\text{Splay}$ 维护二叉查找树。
 +
 +[[https://​loj.ac/​problem/​104|【模板】普通平衡树]]
 +
 +[[https://​loj.ac/​problem/​105|【模板】文艺平衡树]]
 +
 +[[https://​loj.ac/​problem/​10143|「HNOI2002」营业额统计]]
 +
 +[[https://​loj.ac/​problem/​10144|「HNOI2004」宠物收养所]]
 +
 +===== 练习题 =====
 +
 +[[https://​www.luogu.com.cn/​problem/​P4402|「Cerc2007」robotic sort 机械排序]]
 +
 +[[https://​loj.ac/​problem/​106|二逼平衡树(树套树)]]
 +
 +[[http://​www.lydsy.com/​JudgeOnline/​problem.php?​id=2827|bzoj 2827 千山鸟飞绝]]
 +
 +[[http://​www.lydsy.com/​JudgeOnline/​problem.php?​id=4923|「Lydsy1706 月赛」K 小值查询]]
 +
 +===== 参考链接 =====
 +
 +[[https://​oi-wiki.org/​ds/​splay/​|OI Wiki]]
2020-2021/teams/legal_string/lgwza/splay.1598105151.txt.gz · 最后更改: 2020/08/22 22:05 由 lgwza