HNOI2004 宠物收养所(SBT应用&splay解法)

1208: [HNOI2004]宠物收养所

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 1003   Solved: 351
[ Submit][ Status][ Discuss]

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,最后一个领养者没有宠物可以领养)

题目:http://mail.bashu.cn:8080/bs_oj/showproblem?problem_id=2098

分析:这题貌似有很简便的方法,不过还是可以用SBT来搞的,只要标记一下树里面包含的是宠物还是收养者,如果与现在来的这位一样的话就住下,否者找个相近的一起走了。。。只用到插入,删除,前驱,后继,查找

代码:

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int mm=111111;
int L[mm],R[mm],S[mm],V[mm];
int i,x,y,a,b,n,tt,root,kind,ans,sum;
void Right_Rotate(int &t)
{
    int k=L[t];
    L[t]=R[k];
    R[k]=t;
    S[k]=S[t];
    S[t]=S[L[t]]+S[R[t]]+1;
    t=k;
}
void Left_Rotate(int &t)
{
    int k=R[t];
    R[t]=L[k];
    L[k]=t;
    S[k]=S[t];
    S[t]=S[L[t]]+S[R[t]]+1;
    t=k;
}
void maintain(int &t,bool flag)
{
    if(flag)
        if(S[R[R[t]]]>S[L[t]])Left_Rotate(t);
        else if(S[L[R[t]]]>S[L[t]])
            Right_Rotate(R[t]),Left_Rotate(t);
        else return;
    else
        if(S[L[L[t]]]>S[R[t]])Right_Rotate(t);
        else if(S[R[L[t]]]>S[R[t]])
            Left_Rotate(L[t]),Right_Rotate(t);
        else return;
    maintain(L[t],0);
    maintain(R[t],1);
    maintain(t,0);
    maintain(t,1);
}
void Insert(int &t,int v)
{
    if(t)
    {
        ++S[t];
        if(v<V[t])Insert(L[t],v);
        else Insert(R[t],v);
        maintain(t,v>=V[t]);
    }
    else
    {
        S[t=++tt]=1;
        V[t]=v;
        L[t]=R[t]=0;
    }
}
int Delete(int &t,int v)
{
    --S[t];
    if(v==V[t]||v<V[t]&&!L[t]||v>V[t]&&!R[t])
    {
        int tmp=V[t];
        if(!L[t]||!R[t])t=L[t]+R[t];
        else V[t]=Delete(L[t],V[t]+1);
        return tmp;
    }
    else if(v<V[t])return Delete(L[t],v);
    else return Delete(R[t],v);
}
int Find(int t,int v)
{
    while(t&&v!=V[t])
    t=v<V[t]?Find(L[t],v):Find(R[t],v);
    return t;
}
int Rank(int t,int v)
{
    if(!t)return 1;
    if(v<=V[t])return Rank(L[t],v);
    else return S[L[t]]+1+Rank(R[t],v);
}
int Select(int t,int k)
{
    if(k==S[L[t]]+1)return V[t];
    if(k<=S[L[t]])return Select(L[t],k);
    else return Select(R[t],k-1-S[L[t]]);
}
int Pred(int t,int v)
{
    if(!t)return v;
    if(v<=V[t])return Pred(L[t],v);
    else
    {
        int tmp=Pred(R[t],v);
        return v==tmp?V[t]:tmp;
    }
}
int Succ(int t,int v)
{
    if(!t)return v;
    if(v>=V[t])return Succ(R[t],v);
    else
    {
        int tmp=Succ(L[t],v);
        return v==tmp?V[t]:tmp;
    }
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    sum=ans=tt=root=kind=S[0]=0;
    for(i=0;i<n;++i)
    {
        scanf("%d%d",&a,&b);
        if(!sum||a==kind)kind=a,Insert(root,b),++sum;
        else
        {
            if(Find(root,b))Delete(root,b),--sum;
            else
            {
                x=Pred(root,b);
                y=Succ(root,b);
                if(x==b||y==b)x=x+y-b;
                else if(b-x>y-b)x=y;
                ans+=fabs(b-x),Delete(root,x),--sum;
                if(ans>=1000000)ans%=1000000;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

这题用splay来解还是比较麻烦的,因为要删掉节点,每次都把节点的前驱转到根,把后继转到根的子节点,这样要删的点就是根的右儿子的左儿子,直接删掉就行了

开始熟练起来了^_^

代码:

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int mm=88888;
struct splaytree
{
    int c[mm][2],p[mm],s[mm],v[mm];
    int id,size,root,flag,ans;
    void rotate(int x,int f)
    {
        int y=p[x];
        c[y][!f]=c[x][f];
        p[c[x][f]]=y;
        p[x]=p[y];
        if(p[x])c[p[y]][c[p[y]][1]==y]=x;
        c[x][f]=y;
        p[y]=x;
        updata(y);
    }
    void splay(int x,int goal)
    {
        while(p[x]!=goal)
            if(p[p[x]]==goal)rotate(x,c[p[x]][0]==x);
            else
            {
                int y=p[x],f=(c[p[y]][0]==y);
                if(c[y][f]==x)rotate(x,!f);
                else rotate(y,f);
                rotate(x,f);
            }
        updata(x);
        if(!goal)root=x;
    }
    void select(int k,int goal)
    {
        int x=root;
        while(s[c[x][0]]+1!=k)
        {
            if(k<s[c[x][0]]+1)x=c[x][0];
            else k-=(s[c[x][0]]+1),x=c[x][1];
        }
        splay(x,goal);
    }
    void insert(int a)
    {
        int x=root;
        ++size;
        while(c[x][v[x]<a])x=c[x][v[x]<a];
        newnode(c[x][v[x]<a],a);
        p[id]=x;
        splay(id,0);
    }
    void remove(int x)
    {
        splay(x,0);
        int y=s[c[x][0]];
        select(y,0);
        select(y+2,root);
        c[c[root][1]][0]=0;
        updata(c[root][1]);
        updata(root);
        --size;
    }
    void updata(int x)
    {
        s[x]=1+s[c[x][0]]+s[c[x][1]];
    }
    void newnode(int &x,int a)
    {
        x=++id;
        p[x]=c[x][0]=c[x][1]=0;
        s[x]=1;
        v[x]=a;
    }
    void prepare()
    {
        ans=id=root=size=0;
        p[0]=c[0][0]=c[0][1]=v[0]=0;
        newnode(root,-1000000000);
        newnode(c[root][1],1000000000);
        p[id]=root;
        s[root]=2;
    }
    int find(int a)
    {
        int x=root,ret,s=1000000000;
        while(x)
        {
            if(v[x]==a)return x;
            if(v[x]<a)
            {
                if(a-v[x]<=s)s=a-v[x],ret=x;
            }
            else
            {
                if(v[x]-a<s)s=v[x]-a,ret=x;
            }
            x=c[x][v[x]<a];
        }
        return ret;
    }
    void work()
    {
        int a,b,x;
        scanf("%d%d",&a,&b);
        if(a==flag||size==0)insert(b),flag=a;
        else
        {
            x=find(b);
            ans+=(int)fabs(b-v[x]);
            if(ans>=1000000)ans%=1000000;
            remove(x);
        }
    }
}spt;
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n;
    scanf("%d",&n);
    spt.prepare();
    while(n--)spt.work();
    printf("%d\n",spt.ans);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值