【ybt金牌导航4-3-3】【luogu P2286】宠物收养所

宠物收养所

题目链接:ybt金牌导航4-3-3 / luogu P2286

题目大意

有人和动物,会按一定顺序到来。
人和动物能匹配,如果一个到了,然后另一种有好几个,就选特点值相差最小的,如果有两个,就选比它小的。
然后如果另一种没有,就只能等。
匹配会有花费,就是两个特地值相差的大小。

然后问你花费和,对 1e6 取模。

思路

你想想它每个情况会有两种状态,要么是人在等动物,要么是动物在等人。

那你可以看到选择匹配可以用平衡树的前驱和后继来搞,然后匹配完就删除。

那你两种情况看似要两个平衡树,但其实一个够了,因为同一时间内只有一种会存在,而且操作差不多,那我们完全可以只开一个。

然后我这里用的是 splay。
我才知道,你一开始要插入两个边界,就是这样前驱后继就可以判断有没有,而且不会影响答案。

代码

#include<cstdio> #include<iostream> #define mo 1000000 #define ll long long using namespace std; struct Tree { ll l, r, fa, val, size; }tree[3000001]; ll n, X, root, dog, people, Y; ll tot, lef_root, rig_root; ll ans; bool son__p(ll now) {//询问它是否是它父亲的左儿子 return tree[tree[now].fa].l == now; } void rotate(ll now) {//旋转 ll father = tree[now].fa; ll grand = tree[father].fa; ll son = son__p(now) ? tree[now].r : tree[now].l; if (grand) son__p(father) ? tree[grand].l = now : tree[grand].r = now; if (son__p(now)) tree[now].r = father, tree[father].l = son; else tree[now].l = father, tree[father].r = son; tree[now].fa = grand; tree[father].fa = now; if (son) tree[son].fa = father; } void splay(ll x, ll target) {//splay上浮操作 while (tree[x].fa != target) { if (tree[tree[x].fa].fa != target) { son__p(x) == son__p(tree[x].fa) ? rotate(tree[x].fa) : rotate(x); } rotate(x); } if (!target) root = x; } ll find(ll x) {//看一个值在树中的位置 ll now = root; while (now) { if (x == tree[now].val) break; if (x >= tree[now].val) now = tree[now].r; else now = tree[now].l; } if (now != root) splay(now, 0); return now; } void insert(ll x) {//插入点 ll now = root, last = 0; while (now) { last = now; tree[now].size++; if (x < tree[now].val) now = tree[now].l; else now = tree[now].r; } tot++; tree[tot].fa = last; tree[tot].val = x; tree[tot].size = 1; if (x < tree[last].val) tree[last].l = tot; else tree[last].r = tot; splay(tot, 0); } void join(ll small, ll big) {//合并子树 tree[small].fa = tree[big].fa = 0; ll new_root = small; while (tree[new_root].r) new_root = tree[new_root].r; splay(new_root, 0); tree[new_root].r = big; tree[big].fa = new_root; } void delete_(ll x) {//删除节点 splay(x, 0); if (!tree[x].l && tree[x].r) tree[tree[x].r].fa = 0, root = tree[x].r; else if (tree[x].l && !tree[x].r) tree[tree[x].l].fa = 0, root = tree[x].l; else join(tree[x].l, tree[x].r); tree[x].l = tree[x].r = 0; } ll pre(ll x) {//前驱 ll now = find(x); now = tree[now].l; while (tree[now].r) { now = tree[now].r; } return tree[now].val; } ll nxt(ll x) {//后继 ll now = find(x); now = tree[now].r; while (tree[now].l) { now = tree[now].l; } return tree[now].val; } int main() { insert(2147483647);//边界数据 insert(-2147483647); scanf("%lld", &n); for (ll i = 1; i <= n; i++) { scanf("%lld %lld", &X, &Y); if (X == 0) { if (dog >= people) {//不能匹配 insert(Y); } else {//匹配 insert(Y); ll bef = pre(Y), aft = nxt(Y); if (bef == -2147483647 || (bef != -1 && aft != -1 && 1ll * aft - 1ll * Y < 1ll * Y - 1ll * bef)) { ans = (ans + 1ll * aft - 1ll * Y) % mo; delete_(find(aft)); delete_(find(Y)); } else { ans = (ans + 1ll * Y - 1ll * bef) % mo; delete_(find(bef)); delete_(find(Y)); } } dog++; } else { if (dog <= people) {//不能匹配 insert(Y); } else {//匹配 insert(Y); ll bef = pre(Y), aft = nxt(Y); if (bef == -2147483647 || (bef != -1 && aft != -1 && 1ll * aft - 1ll * Y < 1ll * Y - 1ll * bef)) { ans = (ans + 1ll * aft - 1ll * Y) % mo; delete_(find(aft)); delete_(find(Y)); } else { ans = (ans + 1ll * Y - 1ll * bef) % mo; delete_(find(bef)); delete_(find(Y)); } } people++; } } printf("%lld", ans); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_4-3-3.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(36)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示