bzoj1208 [HNOI2004]宠物收养场

这篇博客讨论了如何使用splay数据结构解决一个关于宠物收养场的问题。领养者和宠物按顺序到达,splay用于找到最匹配的配对,从而计算所有领养者的不满意程度总和。文章提到了一种优化技巧,并提供了STL和自定义splay的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.elijahqi.win/2018/02/18/bzoj1208/
题目描述

凡凡开了一间宠物收养场。收养场提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物。

每个领养者都希望领养到自己满意的宠物,凡凡根据领养者的要求通过他自己发明的一个特殊的公式,得出该领养者希望领养的宠物的特点值a(a是一个正整数,a<2^31),而他也给每个处在收养场的宠物一个特点值。这样他就能够很方便的处理整个领养宠物的过程了,宠物收养场总是会有两种情况发生:被遗弃的宠物过多或者是想要收养宠物的人太多,而宠物太少。

被遗弃的宠物过多时,假若到来一个领养者,这个领养者希望领养的宠物的特点值为a,那么它将会领养一只目前未被领养的宠物中特点值最接近a的一只宠物。(任何两只宠物的特点值都不可能是相同的,任何两个领养者的希望领养宠物的特点值也不可能是一样的)如果有两只满足要求的宠物,即存在两只宠物他们的特点值分别为a-b和a+b,那么领养者将会领养特点值为a-b的那只宠物。

收养宠物的人过多,假若到来一只被收养的宠物,那么哪个领养者能够领养它呢?能够领养它的领养者,是那个希望被领养宠物的特点值最接近该宠物特点值的领养者,如果该宠物的特点值为a,存在两个领养者他们希望领养宠物的特点值分别为a-b和a+b,那么特点值为a-b的那个领养者将成功领养该宠物。

一个领养者领养了一个特点值为a的宠物,而它本身希望领养的宠物的特点值为b,那么这个领养者的不满意程度为abs(a-b)。

你得到了一年当中,领养者和被收养宠物到来收养所的情况,请你计算所有收养了宠物的领养者的不满意程度的总和。这一年初始时,收养所里面既没有宠物,也没有领养者。
输入输出格式

输入格式:

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

输出格式:

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

输入样例#1: 复制

5
0 2
0 4
1 3
1 2
1 5

输出样例#1: 复制

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

看到题目后我首先想到两棵splay是非常容易想得到的 那么接下来参考了下巨佬icefox的思路成功用一棵splay搞定 并且参见了常数帝icefox的一部分卡常技巧 因为题目规定我时时刻刻只可能有一种东西在商店里 那么不妨我可以做一个标记来看人在商店还是动物在商店 因为人和动物的操作都相同 可以这么做 然后每次我去splay里找一下前驱后继 就okay icefox巨佬的方法就是我先插入一个数 然后转到根 然后再直接分别向两边孩子一直走下去走到尽头即可 然后删除的时候同理 然后就可以把要删除的那个卡出来直接删除 最后计算答案的时候我看一下前驱是否比后继更接近并且前驱不可以是-inf 否则我就一定是调用后继

前天偷懒了直接上stl搞 没几分钟代码就搞定了 今天还是决定写一写板子熟练下 一写就是这么长时间

stl

#include<set>
#include<cstdio>
#include<algorithm>
#define mod 1000000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
} 
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
set<int> s;int ans,n;
int main(){
//  freopen("bzoj1208.in","r",stdin);
    n=read();s.insert(inf);s.insert(-inf);int op=0;
    for (int i=1;i<=n;++i){
        int a=read(),b=read();
        if (s.size()==2){
            op=a;s.insert(b);continue;
        }
        if (a!=op) {
            set<int>::iterator pre=--s.lower_bound(b),succ=s.lower_bound(b);
            if (b-*pre<=*succ-b&&*pre!=-inf) (ans+=b-*pre)%=mod,s.erase(pre);
            else (ans+=*succ-b)%=mod,s.erase(succ);
        }else s.insert(b);
    }printf("%d\n",ans);
}

splay

#include<cstdio>
#include<algorithm>
#define N 88000
#define mod 1000000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S) {T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int size[N],cnt,ans,v[N],fa[N],c[N][2],root,n;
inline void update(int x){
    size[x]=size[c[x][0]]+size[c[x][1]]+1;
}
inline void rotate(int x,int &tar){
    int y=fa[x],z=fa[y];
    if (y!=tar) c[z][c[z][1]==y]=x;else tar=x;
    int l=c[y][1]==x,r=l^1;
    fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
    c[y][l]=c[x][r];c[x][r]=y;update(y);update(x);
}
inline void splay(int x,int &tar){
    while(x!=tar){
        int y=fa[x],z=fa[y];
        if (y!=tar){
            if (c[y][0]==x^c[z][0]==y) rotate(x,tar);else rotate(y,tar);
        }rotate(x,tar);
    }
}
inline void insert1(int &x,int p,int f){
    if (!x) {x=++cnt;fa[x]=f;size[x]=1;v[x]=p;splay(x,root);return;}
    if (p<v[x]) insert1(c[x][0],p,x);else insert1(c[x][1],p,x);
}
inline int find(int x,int p){
    if (v[x]==p) return x;
    if (p<v[x]) return find(c[x][0],p);else return find(c[x][1],p);
}
inline void del(int x){
    splay(x,root);int pre=c[root][0],succ=c[root][1];
    while(c[pre][1]) pre=c[pre][1];while(c[succ][0]) succ=c[succ][0];
    splay(succ,root);splay(pre,c[root][0]);c[pre][1]=0;update(pre);update(root);    
}
int main(){
//  freopen("bzoj1208.in","r",stdin);
    n=read();insert1(root,-inf,0);insert1(root,inf,0);int op;
    for (int i=1;i<=n;++i){
        int a=read(),b=read();
        if (size[root]==2) {op=a;insert1(root,b,root);continue;}
        if (a==op){insert1(root,b,root);continue;}
        insert1(root,b,root);splay(cnt,root);int pre=c[root][0],succ=c[root][1];
        while(c[pre][1]) pre=c[pre][1];while(c[succ][0]) succ=c[succ][0];del(cnt);
        if (b-v[pre]<=v[succ]-b&&v[pre]!=-inf) (ans+=b-v[pre])%=mod,del(pre);
        else (ans+=v[succ]-b)%=mod,del(succ);
    }printf("%d",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值