(题解)(Splay)NOI2004郁闷的出纳员

题目比较简单,用splay动态维护一个可以整体删除的集合,
不需要打下标,打个上标维护size求kth,
用delta记录偏移即可完成所有操作。
 
调试了两个小时,后来发现因初始工资而走的员工不能算是走的员工,题目中没说明,太坑了。
在splay中,常用INF哨兵,但我没发现有人用null哨兵,我发现null哨兵能够大幅简化代码,同时具有一定的容错性。
程序的容错性非常重要,即使参数正常也最好写个指针检查,不要在乎这点常数,当访问到null甚至是NULL的时候做特殊处理,即使不可能出现这种情况,在很多情况下也能救你一命。
如果我的Splay代码还有可以优化的地方请联系我!!

#include <cstdio>
#include <cstdlib>
#include <climits>
using namespace std;
#define INF (INT_MAX/2)
struct Node {
    int val,size,mul;
    Node *ch[2],*father;
    Node(int _val,int _mul,Node *_null):val(_val),size(_mul),mul(_mul) {
        ch[0]=ch[1]=father=_null;
    }
    void setch(int w,Node *v) {
        ch[w]=v;
        v->father=this;
    }
    void rot() {
        Node *u=father;
        int w=(u->ch[0]==this);
        u->setch(!w,ch[w]);
        u->father->setch(u->father->ch[1]==u,this);
        setch(w,u);
        u->size=u->ch[0]->size+u->ch[1]->size+u->mul;
    }
} *null=new Node(0,0,(Node*)0);
Node *root=null;
 
void splay(Node *v,Node *goodfather)
{
    for(Node *u;(u=v->father)!=goodfather;v->rot())
        if(u->father!=goodfather)
            (((u->ch[0]==v)==(u->father->ch[0]==u->father))?u:v)->rot();
v->size=v->ch[0]->size+v->ch[1]->size+v->mul;
    if(goodfather==null) root=v;
}
 
Node *S_lower(int x)
{
    Node *ans=null;
    for(Node *u=root;u!=null;u=u->ch[u->val<x]) {
        if(u->val==x) return u;
        if(u->val>x) ans=u;
    }
    return ans;
}
 
Node *S_upper(int x)
{
    Node *ans=null;
    for(Node *u=root;u!=null;u=u->ch[u->val<x]) {
        if(u->val==x) return u;
        if(u->val<x) ans=u;
    }
    return ans;
}
 
void s_insert(int goodval)
{
    if(root==null) {root=new Node(goodval,1,null);return;}
    Node *res=null,*u=root;
    while(true) {
        int w=u->val<goodval;
        if(u->val==goodval) {
            ++(res=u)->mul;
            break;
        } else if(u->ch[w]==null) {
            u->setch(w,res=new Node(goodval,1,null));
            break;
        } else u=u->ch[w];
    }
    splay(res,null);
}
 
int s_delete(int l,int r)
{
    splay(S_upper(l),null);
    splay(S_lower(r),root);
    Node *seg=root->ch[1]->ch[0];
    if(seg==null) return 0;
    root->ch[1]->ch[0]=null;
    (root->ch[1])->size=(root->ch[1])->ch[0]->size+(root->ch[1])->ch[1]->size+(root->ch[1])->mul;
    root->size=root->ch[0]->size+root->ch[1]->size+root->mul;
    return seg->size;
}
 
Node *s_kth(int k)
{
    if(k<1) return null;
    Node *u=root;
    while(k<=u->size) {
        int q1=u->ch[0]->size,q2=q1+u->mul;
        if(k<=q1) u=u->ch[0];
        else if(k>q2) {k-=q2;u=u->ch[1];}
        else {splay(u,null);return u;} 
    }
    return null;
}
 
int main()
{
    int case_cnt,minval,num_go=0,delta=0;
    scanf("%d%d",&case_cnt,&minval);
    s_insert(-INF);s_insert(INF);
    while(case_cnt--) {
        char cmd[6];
        int k;
        scanf("%s%d",cmd,&k);
        if(cmd[0]=='I') {
            k-=delta;
            if(k<minval-delta) ;//++num_go;>o<怎么可以这样?!!! 
            else s_insert(k);
        } else if(cmd[0]=='A') {
            delta+=k;
        } else if(cmd[0]=='S') {
            delta-=k;
            num_go+=s_delete(-INF,minval-delta);//!!!!!!
        } else if(cmd[0]=='F') {
            Node *v=s_kth(root->size-2-k+1+1);
            if(v==null || v->val>=INF || v->val<=-INF) printf("-1\n");
            else printf("%d\n",v->val+delta);
        }
    }
    printf("%d\n",num_go);
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值