[lnsyoj284/luoguP2286/HNOI2004]宠物收养场

题意

原题链接
每个宠物和领养人有一个对应的特点值\(a\),当领养人过多时,若添加一个特点值为\(a_p\)的宠物,则查询收养店中特点值最接近\(a_p\),为\(a_q\)的领养人(\(<a_p\)的值优先),将答案累加\(|a_q - a_p|\),然后删除该领养人,否则在收养店中添加一个领养人。反之亦然。求最终的答案\(\bmod 1000000\)的结果,保证不存在两个特点值相同的领养人或两个特点值相同的宠物。

sol

归纳题意,我们发现我们需要四个操作:

  1. 插入一个数
  2. 删除一个数
  3. 查询前驱
  4. 查询后继

这四个操作可以直接使用Treap完成,具体请移步[lnsyoj118/luoguP3369]普通平衡树
需要注意的是,本题中的前驱/后继与P3369所述不完全相同,本题中前驱/后继可以与查询数\(x\)相等,因此在查询时需要做一定的修改
具体地,以查询前驱为例,在\(x<u.key\)时,说明前驱不存在于右子树,否则说明前驱可能为该节点或存在于右子树,查询后继同理。
本题保证值不重复,因此不需要使用\(cnt\),同时,本题不需要查询排名,因此不需要使用\(size\)。但是,由于本题需要判断Treap是否为空,因此需要单独开两个变量来记录当前Treap内存储的数据类型及线段树中的节点数量(不包含哨兵节点)

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>

using namespace std;
typedef long long LL;

const int N = 80005, mod = 1000000;
const LL INF = 1e18;

struct node{
    int l, r;
    LL key, val;
}tr[N];

int n;
int root, size, idx;
int state;
LL ans;

int create(LL key){
    tr[ ++ idx].key = key;
    tr[idx].val = rand();
    return idx;
}

void zig(int &u){
    int p = tr[u].l;
    tr[u].l = tr[p].r, tr[p].r = u, u = p;
}

void zag(int &u){
    int p = tr[u].r;
    tr[u].r = tr[p].l, tr[p].l = u, u = p;
}

void build(){
    create(-INF), create(INF);
    tr[1].r = 2;
    root = 1;
}

void insert(int &u, LL key){
    if (!u) u = create(key);
    else {
        if (key < tr[u].key){
            insert(tr[u].l, key);
            if (tr[tr[u].l].val > tr[u].val) zig(u);
        }
        else {
            insert(tr[u].r, key);
            if (tr[tr[u].r].val > tr[u].val) zag(u);
        }
    }
}

void erase(int &u, LL key){
    if (!u) return ;
    else if (key == tr[u].key) {
        if (tr[u].l || tr[u].r){
            if (!tr[u].r || tr[tr[u].l].val > tr[tr[u].r].val){
                zig(u);
                erase(tr[u].r, key);
            }
            else {
                zag(u);
                erase(tr[u].l, key);
            }
        }
        else u = 0;
    }
    else if (key < tr[u].key) erase(tr[u].l, key);
    else erase(tr[u].r, key);
}

LL get_prev(int u, LL key){
    if (!u) return -INF;
    if (key < tr[u].key) return get_prev(tr[u].l, key);
    else return max(tr[u].key, get_prev(tr[u].r, key));
}

LL get_next(int u, LL key){
    if (!u) return INF;
    if (key > tr[u].key) return get_next(tr[u].r, key);
    else return min(tr[u].key, get_next(tr[u].l, key));
}

int main(){
    scanf("%d", &n);
    build();
    while (n -- ){
        int op;
        LL key;
        scanf("%d%lld", &op, &key);
        if (state == -1) {
            state = op;
            insert(root, key);
            size ++ ;
        }
        else if (state == op) insert(root, key), size ++ ;
        else {
            LL pr = get_prev(root, key), ne = get_next(root, key);
            LL res = min(abs(key - pr), abs(key - ne));
            LL del;
            if (res == abs(key - pr)) del = pr;
            else del = ne;
            erase(root, del);
            ans = (ans + res) % mod;
            size -- ;
            if (!size) state = -1;
        }
    }
    printf("%lld\n", ans);

    return 0;
}

蒟蒻犯的若至错误

  • 插入节点时,没有将节点地址赋给新开的节点
posted @ 2024-06-15 22:04  是一只小蒟蒻呀  阅读(14)  评论(0编辑  收藏  举报