Luogu P2234+P2286 (Splay性质

Luogu P2234

  • 记住性质才能更好利用: Splay的查询前驱和后继操作
    • t r e e . i n s ( v a l ) tree.ins(val) tree.ins(val)插入后会 S p l a y Splay Splay到根节点
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 1e5+10;

int rt,tot,fa[maxn],ch[maxn][2],cnt[maxn],size[maxn];
int val[maxn];

struct Splay {
    void maintain(int x) { size[x] = size[ch[x][0]] + size[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] = size[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(y);
        maintain(x);
    }
    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) {
            // 恰好k == 当前值
            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;
            }
        }
    }
    // 给一个数k 查它的排名
    int rk(int k) {
        int res = 0,cnr = rt;
        while (1) {
            if (k < val[cnr])
                cnr = ch[cnr][0];
            else {
                res += size[ch[cnr][0]];
                if (k == val[cnr]) {
                    splay(cnr);
                    return res+1;
                }
                // 加上该节点的值
                res += cnt[cnr];
                cnr = ch[cnr][1];
            }
        }
    }
    // 给出排名k, 查询数
    int kth(int k) {
        int cnr = rt;
        while (1) {
            if (ch[cnr][0] && k <= size[ch[cnr][0]])
                cnr = ch[cnr][0];
            else {
                k -= size[ch[cnr][0]] + cnt[cnr];
                if (k <= 0) return val[cnr];
                cnr = ch[cnr][1];
            }
        }
    }
    // 根节点左子树最大值
    int pre() {
        int cnr = ch[rt][0];
        while(ch[cnr][1]) cnr = ch[cnr][1];
        return cnr;
    }
    // 根节点右子树最小值
    int nxt() {
        int cnr = ch[rt][1];
        while(ch[cnr][0]) cnr = ch[cnr][0];
        return cnr;
    }
    // 删除 值为k的节点
    void del(int k) {
        // 查k的排名将它旋转到根节点
        rk(k);
        // 多个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 ;
        }
        // 否则将x的左子树最大值旋转上来作为新 ,删除根节点后右子树接上来
        int x = pre(),cnr = rt;
        splay(x);
        // 修改原右子树的父亲节点
        fa[ch[cnr][1]] = x;
        // 新左子树
        ch[x][1] = ch[cnr][1];
        clear(cnr);
        maintain(rt);
    }
} tree;

int n,ans,temp;

int main() {
    cin >> n;
    for (int i=1; i<=n; ++i) {
        cin >> temp;
        tree.ins(temp); // 插入后splay到了根节点
        if (i==1)
            ans += temp;
        else {
            if (cnt[rt]<=1) {
                int left = tree.pre();
                int right = tree.nxt();
                if (cnt[left] && cnt[right])
                    ans += min( abs(temp-val[left]) , abs(temp-val[right]) );
                else if (cnt[left])
                    ans += abs(temp-val[left]);
                else if (cnt[right])
                    ans += abs(temp-val[right]);
            }
        }
    }
    cout << ans << endl;
}

Luogu P2286

  1. 开2个树分别存(人和宠物)

  2. 同一时间呆在收养所中的,要么全是宠物,要么全是领养者, 所以一个树就可以处理,判断哪一个时刻存转为村宠物或者领养者


  • 处理mod
  • 处理 可能2子树其中有一个不存在的情况
  • 所有值都唯一

套在splay板子上 代码有点长

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;

int rt,tot,fa[maxn],ch[maxn][2],val[maxn],cnt[maxn],size[maxn];
int mod = 1e6;

struct Splay {
    void maintain(int x) { size[x] = size[ch[x][0]] + size[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] = size[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(y);
        maintain(x);
    }
    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) {
            // 恰好k == 当前值
            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;
            }
        }
    }
    // 给一个数k 查它的排名
    int rk(int k) {
        int res = 0,cnr = rt;
        while (1) {
            if (k < val[cnr])
                cnr = ch[cnr][0];
            else {
                res += size[ch[cnr][0]];
                if (k == val[cnr]) {
                    splay(cnr);
                    return res+1;
                }
                // 加上该节点的值
                res += cnt[cnr];
                cnr = ch[cnr][1];
            }
        }
    }
    // 给出排名k, 查询数
    int kth(int k) {
        int cnr = rt;
        while (1) {
            if (ch[cnr][0] && k <= size[ch[cnr][0]])
                cnr = ch[cnr][0];
            else {
                k -= size[ch[cnr][0]] + cnt[cnr];
                if (k <= 0) return val[cnr];
                cnr = ch[cnr][1];
            }
        }
    }
    // 根节点左子树最大值
    int pre() {
        int cnr = ch[rt][0];
        while(ch[cnr][1]) cnr = ch[cnr][1];
        return cnr;
    }
    // 根节点右子树最小值
    int nxt() {
        int cnr = ch[rt][1];
        while(ch[cnr][0]) cnr = ch[cnr][0];
        return cnr;
    }
    // 删除 值为k的节点
    void del(int k) {
        // 查k的排名将它旋转到根节点
        rk(k);
        // 多个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 ;
        }
        // 否则将x的左子树最大值旋转上来作为新 ,删除根节点后右子树接上来
        int x = pre(),cnr = rt;
        splay(x);
        // 修改原右子树的父亲节点
        fa[ch[cnr][1]] = x;
        // 新左子树
        ch[x][1] = ch[cnr][1];
        clear(cnr);
        maintain(rt);
    }
} tree;

int n,all,hope,k,ans;

int main() {
    cin >> n;
    while(n--) {
        cin >> k >> hope;
        if (all == 0)
            tree.ins(hope);
        if (all > 0) {
            if (k==0)
                tree.ins(hope);
            else {
                tree.ins(hope);
                int left = tree.pre(),right = tree.nxt();
                int lv = val[left],rv = val[right];
                if (cnt[left] && cnt[right]) {
                    if (abs(hope - lv) == abs(hope - rv)) {
                        ans += abs(hope - lv);
                        ans = ans % mod;
                        tree.del(lv);
                    } else {
                        int temp = min( abs(hope-lv) , abs(hope-rv) );
                        ans += temp;
                        ans = ans % mod;
                        tree.del( (temp==abs(hope-lv)) ? lv : rv );
                    }
                } else if (cnt[left]) {
                    //cout << " lv " << lv << endl;
                    ans += abs(hope - lv);
                    ans = ans % mod;
                    tree.del(lv);
                } else if (cnt[right]) {
                    //cout << " rv " << rv << " hope " << hope  << " ans " << ans  << endl;
                    ans += abs(hope - rv);
                    ans = ans % mod;
                    tree.del(rv);
                }

                tree.del(hope);
            }
        }
        if (all < 0) { // 顾客树
            if (k == 1) // 插入顾客
                tree.ins(hope);
            else {
                tree.ins(hope);
                int left = tree.pre(),right = tree.nxt();
                int lv = val[left],rv = val[right];
                if (cnt[left] && cnt[right]) {
                    if (abs(hope - lv) == abs(hope - rv)) {
                        ans += abs(hope - lv);
                        ans = ans % mod;
                        tree.del(lv);
                    } else {
                        int temp = min( abs(hope-lv) , abs(hope-rv) );
                        ans += temp;
                        ans = ans % mod;
                        tree.del( (temp==abs(hope-lv)) ? lv : rv );
                    }
                } else if (cnt[left]) {
                    ans += abs(hope - lv);
                    ans = ans % mod;
                    tree.del(lv);
                } else if (cnt[right]) {
                    ans += abs(hope - rv);
                    ans = ans % mod;
                    tree.del(rv);
                }
                tree.del(hope);
            }
        }
        all += (k==0 ? 1 : -1);
    }
    cout << ans << endl;
    return 0;
}

  • 重写 避免代码复用
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;

int rt,tot,fa[maxn],ch[maxn][2],val[maxn],cnt[maxn],size[maxn];
int mod = 1e6;

struct Splay {
    void maintain(int x) { size[x] = size[ch[x][0]] + size[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] = size[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(y);
        maintain(x);
    }
    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) {
            // 恰好k == 当前值
            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;
            }
        }
    }
    // 给一个数k 查它的排名
    int rk(int k) {
        int res = 0,cnr = rt;
        while (1) {
            if (k < val[cnr])
                cnr = ch[cnr][0];
            else {
                res += size[ch[cnr][0]];
                if (k == val[cnr]) {
                    splay(cnr);
                    return res+1;
                }
                // 加上该节点的值
                res += cnt[cnr];
                cnr = ch[cnr][1];
            }
        }
    }
    // 给出排名k, 查询数
    int kth(int k) {
        int cnr = rt;
        while (1) {
            if (ch[cnr][0] && k <= size[ch[cnr][0]])
                cnr = ch[cnr][0];
            else {
                k -= size[ch[cnr][0]] + cnt[cnr];
                if (k <= 0) return val[cnr];
                cnr = ch[cnr][1];
            }
        }
    }
    // 根节点左子树最大值
    int pre() {
        int cnr = ch[rt][0];
        while(ch[cnr][1]) cnr = ch[cnr][1];
        return cnr;
    }
    // 根节点右子树最小值
    int nxt() {
        int cnr = ch[rt][1];
        while(ch[cnr][0]) cnr = ch[cnr][0];
        return cnr;
    }
    // 删除 值为k的节点
    void del(int k) {
        // 查k的排名将它旋转到根节点
        rk(k);
        // 多个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 ;
        }
        // 否则将x的左子树最大值旋转上来作为新 ,删除根节点后右子树接上来
        int x = pre(),cnr = rt;
        splay(x);
        // 修改原右子树的父亲节点
        fa[ch[cnr][1]] = x;
        // 新左子树
        ch[x][1] = ch[cnr][1];
        clear(cnr);
        maintain(rt);
    }
} tree;

int n,all,hope,k,ans;

int main() {
    cin >> n;
    while(n--) {
        cin >> k >> hope;
        if (all == 0)
            tree.ins(hope);
        bool flag = 0;
        if (all>0 && k==0) {
            tree.ins(hope);
            flag = 1;
        }
        if (all<0 && k==1) {
            tree.ins(hope);
            flag = 1;
        }
        if (flag) {
            all += (k==0?1:-1);
            continue;
        }
        tree.ins(hope);
        int left = tree.pre(),right = tree.nxt();
        int lv = val[left],rv = val[right];
        if (cnt[left] && cnt[right]) {
            if (abs(hope - lv) == abs(hope - rv)) {
                ans = (abs(hope -lv) + ans)%mod;
                tree.del(lv);
            } else {
                int temp = min( abs(hope-lv),abs(hope-rv) );
                ans = (ans + temp) % mod;
                tree.del((temp==abs(hope-lv))?lv:rv);
            }
        } else if (cnt[left]) {
            ans = (ans + abs(hope - lv)) % mod;
            tree.del(lv);
        } else if (cnt[right]) {
            ans = (ans + abs(hope - rv)) % mod;
            tree.del(rv);
        }
        tree.del(hope);
        all += (k==0?1:-1);
    }
    cout << ans << endl;
    return 0;
}
  • 一定要锻炼将题目所求转化到学过的东西???目前面临的最大问题,不然白学了 艹
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个示例的Splay Tree的Python代码实现: ```python class SplayTree: class Node: def __init__(self, key): self.key = key self.parent = None self.left = None self.right = None def __init__(self): self.root = None def _splay(self, x): while x.parent: p = x.parent g = p.parent if g: if (x == p.left) == (p == g.left): self._rotate(p) else: self._rotate(x) self._rotate(x) else: self._rotate(x) def _rotate(self, x): p = x.parent if not p: return g = p.parent if p.left == x: b = x.right x.right = p p.left = b else: b = x.left x.left = p p.right = b if b: b.parent = p x.parent = g if not g: self.root = x elif g.left == p: g.left = x else: g.right = x p.parent = x def find(self, key): x = self.root while x: if key < x.key: x = x.left elif key > x.key: x = x.right else: self._splay(x) return x return None def insert(self, key): if not self.root: self.root = self.Node(key) return x = self.root while True: if key < x.key: if x.left: x = x.left else: x.left = self.Node(key) x.left.parent = x self._splay(x.left) return elif key > x.key: if x.right: x = x.right else: x.right = self.Node(key) x.right.parent = x self._splay(x.right) return else: self._splay(x) return def delete(self, key): x = self.find(key) if not x: return if not x.left: b = x.right self._transplant(x, x.right) elif not x.right: b = x.left self._transplant(x, x.left) else: y = x.right while y.left: y = y.left if y.parent != x: self._transplant(y, y.right) y.right = x.right y.right.parent = y self._transplant(x, y) y.left = x.left y.left.parent = y b = y del x if b: self._splay(b) def _transplant(self, u, v): if not u.parent: self.root = v elif u == u.parent.left: u.parent.left = v else: u.parent.right = v if v: v.parent = u.parent ``` 这份代码实现了Splay Tree的基本操作,包括查找、插入和删除。其中,查找操作会将查找到的节点通过splay操作旋转到根节点,以提高查找效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值