题意
原题链接
每个宠物和领养人有一个对应的特点值\(a\),当领养人过多时,若添加一个特点值为\(a_p\)的宠物,则查询收养店中特点值最接近\(a_p\),为\(a_q\)的领养人(\(<a_p\)的值优先),将答案累加\(|a_q - a_p|\),然后删除该领养人,否则在收养店中添加一个领养人。反之亦然。求最终的答案\(\bmod 1000000\)的结果,保证不存在两个特点值相同的领养人或两个特点值相同的宠物。
sol
归纳题意,我们发现我们需要四个操作:
- 插入一个数
- 删除一个数
- 查询前驱
- 查询后继
这四个操作可以直接使用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;
}
蒟蒻犯的若至错误
- 插入节点时,没有将节点地址赋给新开的节点