BZOJ 1208: [HNOI2004]宠物收养所 (Treap)

BZOJ 1208: [HNOI2004]宠物收养所

题目概述:

有一家宠物收养所,提供两种服务:收养主人遗弃的宠物和让新主人领养宠物.
宠物收养所中总是会有两种情况发生:遗弃宠物过多和领养宠物人过多.
1.遗弃宠物多时,若来一个领养人,领养最接近要求的宠物,若有多只,优先选择小的.
2.领养人多时,若来一只宠物,领养要求最接近的领养,若有多人,优先选择小的.
求领养的宠物的人的不满意度之和,不满意度为领养要求和宠物特点值的差值绝对值.

题目分析:

(又是一道Treap练手题,能一次AC,开心)

结点要么是宠物,要么是人,且在同一时刻仅有一种结点.
所以可以用一个变量state记录Treap当前结点种类.
相同,则插入;不同,则先找前驱或者后继,再删除结点.
需要注意结点为空时,选择插入结点.

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long ll;
const int MOD=1000000;
const ll INF=(1ll<<60);
const int maxn=80000+10;

#define lc ch[u][0]
#define rc ch[u][1]

ll key[maxn];
int state,root,id;//state判断结点种类
int ch[maxn][2],rnd[maxn];

void rotate(int& u,int d)
{
    int v=ch[u][d^1];
    ch[u][d^1]=ch[v][d];
    ch[v][d]=u;u=v;
}

void insert(int& u,int val)
{
    if(!u) {
        u=++id;
        rc=lc=0;
        key[u]=val;
        rnd[u]=rand();
        return ;
    }
    int d=val>key[u];
    insert(ch[u][d],val);
    if(rnd[u]>rnd[ch[u][d]]) rotate(u,d^1);
}

bool remove(int& u,int val)
{
    if(!u) return false;
    if(val==key[u]) {
        if(!lc||!rc) return u=lc?lc:rc,true;
        if(rnd[lc]>rnd[rc]) rotate(u,0);
        else rotate(u,1);
        return remove(u,val);
    }
    int d=val>key[u];
    return remove(ch[u][d],val);
}

ll find_pre(int val)
{
    int u=root;
    ll pre=-INF;
    while(u) {
        if(val>key[u]) pre=key[u],u=rc;
        else u=lc;
    }
    return pre;
}

ll find_nxt(int val)
{
    int u=root;
    ll nxt=INF;
    while(u) {
        if(val<key[u]) nxt=key[u],u=lc;
        else u=rc;
    }
    return nxt;
}

int query(int val)
{
    ll pre=find_pre(val);
    ll nxt=find_nxt(val);
    if(nxt-val<val-pre) return remove(root,nxt),(nxt-val)%MOD;
    else return remove(root,pre),(val-pre)%MOD;
}

int main()
{
    int n,a,b,ans=0;
    scanf("%d",&n);
    while(n--) {
        scanf("%d%d",&a,&b);
        if(!root||a==state) insert(root,b),state=a;
        else (ans+=query(b))%=MOD;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值