宠物收养所
题目链接: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;
}