BZOJ 1208: [HNOI2004]宠物收养所 set,或者splay

时间:2020-12-24 20:43:18

Description

最近,阿Q开了一间宠物收养所。收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物。每个领养者都希望领养到自己满意的宠物,阿Q根据领养者的要求通过他自己发明的一个特殊的公式,得出该领养者希望领养的宠物的特点值a(a是一个正整数,a<2^31),而他也给每个处在收养所的宠物一个特点值。这样他就能够很方便的处理整个领养宠物的过程了,宠物收养所总是会有两种情况发生:被遗弃的宠物过多或者是想要收养宠物的人太多,而宠物太少。 1. 被遗弃的宠物过多时,假若到来一个领养者,这个领养者希望领养的宠物的特点值为a,那么它将会领养一只目前未被领养的宠物中特点值最接近a的一只宠物。(任何两只宠物的特点值都不可能是相同的,任何两个领养者的希望领养宠物的特点值也不可能是一样的)如果有两只满足要求的宠物,即存在两只宠物他们的特点值分别为a-b和a+b,那么领养者将会领养特点值为a-b的那只宠物。 2. 收养宠物的人过多,假若到来一只被收养的宠物,那么哪个领养者能够领养它呢?能够领养它的领养者,是那个希望被领养宠物的特点值最接近该宠物特点值的领养者,如果该宠物的特点值为a,存在两个领养者他们希望领养宠物的特点值分别为a-b和a+b,那么特点值为a-b的那个领养者将成功领养该宠物。 一个领养者领养了一个特点值为a的宠物,而它本身希望领养的宠物的特点值为b,那么这个领养者的不满意程度为abs(a-b)。【任务描述】你得到了一年当中,领养者和被收养宠物到来收养所的情况,希望你计算所有收养了宠物的领养者的不满意程度的总和。这一年初始时,收养所里面既没有宠物,也没有领养者。
Input

第一行为一个正整数n,n<=80000,表示一年当中来到收养所的宠物和领养者的总数。接下来的n行,按到来时间的先后顺序描述了一年当中来到收养所的宠物和领养者的情况。每行有两个正整数a, b,其中a=0表示宠物,a=1表示领养者,b表示宠物的特点值或是领养者希望领养宠物的特点值。(同一时间呆在收养所中的,要么全是宠物,要么全是领养者,这些宠物和领养者的个数不会超过10000个)
Output

仅有一个正整数,表示一年当中所有收养了宠物的领养者的不满意程度的总和mod 1000000以后的结果。
Sample Input
5

0 2

0 4

1 3

1 2

1 5

Sample Output
3

(abs(3-2) + abs(2-4)=3,最后一个领养者没有宠物可以领养)

方法1: 直接上Splay。。。。。。。这个Splay我调了一个下午,原因是我开始插入两个值-inf和inf的时候inf在用set写的时候写成了Ox7fffffff,直接溢出错误,我一直以为Splay写错了,改了一下午,简直不要太伤。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
const int mod = 1000000;
const int inf = 1e9;
namespace SplayTree{
int siz[maxn], fa[maxn], val[maxn];
int ch[maxn][2], root, tot, ans;
void newnode(int &rt, int father, int k){
rt = ++tot;
siz[rt] = 1, fa[rt] = father;
val[rt] = k;
ch[rt][0] = ch[rt][1] = 0;
}
void pushup(int rt){
int l = ch[rt][0], r = ch[rt][1];
siz[rt] = siz[l] + siz[r] + 1;
}
void rotate(int x){ //旋转,把x转到根节点,kind为1代表右旋,kind为0代表左旋
int y = fa[x], kind = ch[y][1] == x;
ch[y][kind] = ch[x][!kind];
fa[ch[y][kind]] = y;
ch[x][!kind] = y;
fa[x] = fa[y];
fa[y] = x;
ch[fa[x]][ch[fa[x]][1] == y] = x;
pushup(y), pushup(x);
}
void splay(int x, int goal) //伸展操作,把根为r的子树调整为goal
{
while(fa[x] != goal)
{
int y = fa[x], z = fa[y];
if(z == goal) rotate(x);
else if((ch[y][1] == x) == (ch[z][1] == y)) rotate(y), rotate(x);
else rotate(x), rotate(x);
}
if(goal == 0) root = x;
}
int getpre(int x){ //求前驱
int t = ch[x][0];
while(ch[t][1]) t = ch[t][1];
return t;
}
int getsub(int x){ //求后继
int t = ch[x][1];
while(ch[t][0]) t = ch[t][0];
return t;
}
int find(int v){ //查找v在SPT中的位置
int x = root;
while(x && val[x] != v) x = ch[x][val[x] < v];
return x;
}
void del(int v){//删除一个数v
int x = find(v);
if(!x) return;
splay(x, 0); //把待删除节点旋转到根
if(!ch[root][0] || !ch[root][1]){
root = ch[root][0] + ch[root][1], fa[root] = 0;
}else{//若根有两个儿子,把根的后继旋转到根的右儿子,再提到根
int t = getsub(root);
splay(t, root);
ch[ch[root][1]][0] = ch[root][0], root = ch[root][1];
fa[ch[root][0]] = root, fa[root] = 0;
pushup(root);
}
}
bool insert(int v){ //插入一个数v
if(!root) newnode(root, 0, v);
else{
int x = root;
while(ch[x][val[x] < v]){
if(val[x] == v){
splay(x, 0);
return false;
}
x = ch[x][val[x] < v];
}
newnode(ch[x][val[x] < v], x, v);
splay(ch[x][val[x] < v], 0);
}
return true;
}
}
using namespace SplayTree;

int n, a, b, type;
int main(){
root = ans = tot = 0;
insert(-inf);
insert(inf);
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d%d", &a, &b);
if(siz[root] == 2){
type = a;
}
if(type == a) insert(b);
else{
insert(b);
int c = val[getpre(root)], d = val[getsub(root)];
c = b - c <= d - b ? c : d;
ans = (ans % mod + abs(b - c) % mod) % mod;
del(b);
del(c);
}
}
printf("%d\n", ans);
return 0;
}

解题方法2,用set或者平衡树,思路一样

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x7fffffff;
const int mod = 1000000;
set <int> s;
int n, t, ans;
void solve(int x){
set <int> :: iterator l = --s.lower_bound(x), r = s.lower_bound(x);
if(x - *l <= *r - x && *l != -inf){
ans += x - *l;
s.erase(l);
}
else{
ans += *r - x;
s.erase(r);
}
ans %= mod;
}
int main(){
s.insert(inf); s.insert(-inf);
scanf("%d", &n);
for(int i = 1; i <= n; i++){
int a, b;
scanf("%d%d", &a, &b);
if(s.size() == 2){
t = a;
s.insert(b);
}
else if(a == t) s.insert(b);
else solve(b);
}
printf("%d\n", ans);
}