P2286 [HNOI2004]宠物收养场

题目链接

这道题应该很快看出是平衡树吧。对于每次操作,相当于是在维护好的平衡树上找前驱和后继。一开始我想的是维护两棵平衡树,一棵宠物树,一棵是人树。但是我这样搞就非常傻逼,而且非常难调。其实只用维护一棵平衡树就够了,用一个变量来记录当前是宠物多还是人多,在树上查询前驱和后继就可以了。

坑点:

1.一开始要加入两个哨兵节点,不然容易出玄学错误。

2.求和的时候要加绝对值abs。

3.第八个点是0,但是我输出的是负数??需要特判才能过。

代码:

P2286 [HNOI2004]宠物收养场
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+7;
const int mod=1000000;
const int inf=0x7fffffff;
int n;
int opt,a;
int flag;
long long sum;
int fa1[maxn],key1[maxn],ch1[maxn][2],cnt1[maxn],siz1[maxn];
int rt1,sz1;
bool check1(int x){
    return ch1[fa1[x]][1]==x;
}
void pushup1(int x){
    siz1[x]=siz1[ch1[x][1]]+siz1[ch1[x][0]]+cnt1[x];
}
void clear1(int x){
    siz1[x]=cnt1[x]=fa1[x]=ch1[x][0]=ch1[x][1]=key1[x]=0;
} 
void rotate1(int x){
    int y=fa1[x],z=fa1[y],who=check1(x);
    ch1[y][who]=ch1[x][who^1];
    fa1[ch1[y][who]]=y;
    ch1[x][who^1]=y;
    fa1[y]=x,fa1[x]=z;
    if(z) ch1[z][ch1[z][1]==y]=x;
    pushup1(y);pushup1(x);
}
void splay1(int x,int goal){
    for(int f;(f=fa1[x])!=goal;rotate1(x)){
        if(fa1[f]!=goal) rotate1((check1(x)==check1(f))?f:x);
    }
    if(!goal) rt1=x;
}
void insert1(int x){
    if(!rt1){
        rt1=++sz1;
        cnt1[sz1]=siz1[sz1]=1;
        key1[sz1]=x;
        return;
    }
    int now=rt1,f=0;
    while(1){
        if(key1[now]==x){
            cnt1[now]++;
            pushup1(f);
            pushup1(now);
            splay1(now,0);
            return;
        }
        f=now,now=ch1[now][x>key1[now]];
        if(!now){
            sz1++;
            fa1[sz1]=f;
            ch1[f][x>key1[f]]=sz1;
            siz1[sz1]=cnt1[sz1]=1;
            key1[sz1]=x;
            pushup1(f);
            splay1(sz1,0);
            return;
        }
    }
}
int pre1(){
    int now=ch1[rt1][0];
    while(ch1[now][1]) now=ch1[now][1];
    return now;
}
int nxt1(){
    int now=ch1[rt1][1];
    while(ch1[now][0]) now=ch1[now][0];
    return now;
}
int rk1(int x){
    int now=rt1,ans=0;
    while(1){
        if(x<key1[now]) now=ch1[now][0];
        else{
            ans+=siz1[ch1[now][0]];
            if(x==key1[now]){
                splay1(now,0);
                return ans+1;
            }
            ans+=cnt1[now];
            now=ch1[now][1];
        }
    }
} 
void del1(int x){
    rk1(x);
    if(cnt1[rt1]>1){
        cnt1[rt1]--;
        pushup1(rt1);
        return;
    }
    if(!ch1[rt1][1]&&!ch1[rt1][0]){
        clear1(rt1);
        rt1=0;
        return;
    }
    if(!ch1[rt1][1]){
        int ort=rt1;
        rt1=ch1[rt1][0];
        fa1[rt1]=0;
        clear1(ort);
        return;
    }
    else if(!ch1[rt1][0]){
        int ort=rt1;
        rt1=ch1[rt1][1];
        fa1[rt1]=0;
        clear1(ort);
        return;
    }
    int ort=rt1,left=pre1();
    splay1(left,0);
    fa1[ch1[ort][1]]=rt1;
    ch1[rt1][1]=ch1[ort][1];
    clear1(ort);
    pushup1(rt1);
}
int tot;//判断宠物多还是人多 
int main(){
    scanf("%d",&n);
    insert1(inf);
    insert1(-inf);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&opt,&a);
        if(!tot) insert1(a);
        if(tot>0){//pet
            if(opt==0) insert1(a);
            else{
                insert1(a);
                int ans1=key1[pre1()];
                int ans2=key1[nxt1()];
                if(abs(a-ans1)<=abs(ans2-a)){
                    sum=(long long)(sum+abs(a-ans1))%mod;
                    del1(ans1);
                } 
                else{
                    sum=(long long)(sum+abs(ans2-a))%mod;
                    del1(ans2);
                } 
                del1(a);
            }
        }
        if(tot<0){//customers
            if(opt==1) insert1(a);
            else{ 
                insert1(a);
                int ans1=key1[pre1()];
                int ans2=key1[nxt1()];
                if(abs(a-ans1)<=abs(ans2-a)){
                    sum=(long long)(sum+abs(a-ans1))%mod;
                    del1(ans1);
                }
                else{
                    sum=(long long)(sum+abs(ans2-a))%mod;
                    del1(ans2);
                } 
                del1(a);
            }
        }
        tot=tot+(opt==0?1:-1); 
    }
    if(sum<0) printf("0\n");//特判,不然有一个点会wa,我也不知道为什么 
    else printf("%lld\n",sum);
    return 0;
}
View Code

 

上一篇:解题报告


下一篇:Ch1_3(初尝试自定义函数)