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
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值