[Splay模板题]POJ3481 num巧用

three kind of operation:  
op1: insert client whose id is x and priority is p to queue  
op2: get the client with **highest** priority and drop him from the queue  
op2: get the client with **lowest** priority abd drio him from the queue  
此处由于每种权值的顾客只会有一个,则所有点的num都一定为1,没有用了,可以用num表示某权值对应的顾客id;
要注意的是sum的修改此时不根据各点的num来了,而是仅+1(newnode(),update(),insert()中对sum有修改)

#include <cstdio>
#define lchild(x) (T[x].ch[0])
#define rchild(x) (T[x].ch[1])
#define fa(x) (T[x].fa)
#define root (T[0].ch[1])
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int n,cnt;
ll ans=1e18;
struct node{
    int val,ch[2],num,sz,fa;//权值,孩子,该权值个数,子树中所有权值个数,父亲
}T[maxn];
inline int newnode(int u,int fa,int num){
    T[++cnt].fa=fa;T[cnt].val=u;//是cnt不是u QAQ
    T[cnt].sz=1;T[cnt].num=num;
    return cnt;
}
bool ident(int x){return lchild(fa(x))==x?0:1;}
void link(int x,int fa,int dir){T[fa(x)=fa].ch[dir]=x;}
void update(int x){
     T[x].sz=T[lchild(x)].sz+T[rchild(x)].sz+1;}
void rotate(int x){
    int dir=ident(x),Y=fa(x),Z=fa(Y);
    link(T[x].ch[dir^1],Y,dir);
    link(x,Z,ident(Y));
    link(Y,x,dir^1);
    update(Y);update(x);//此处要先更新Y再更新x,因为此时Y是x的孩子
}
void splay(int x,int to){//x==to也可以处理
    to=fa(to);
    while(fa(x)!=to){
        if(fa(fa(x))==to)rotate(x);
        else if(ident(x)==ident(fa(x))) rotate(fa(x)),rotate(x);
        else rotate(x),rotate(x);
    }
}
void insert(int x,int idx){//权重为x的,个数表示身份标识
    if(!root) root=newnode(x,0,idx);
    else{
        int now=root;
        while(1){
            T[now].sz++;//T[now].sz+=idx;加个数
            if(T[now].val==x){ T[now].num+=idx,splay(now,root);return;}
            int dir=T[now].val<x?1:0;
            if(!T[now].ch[dir]){
                splay(T[now].ch[dir]=newnode(x,now,idx),root);
                return;
            }
            now=T[now].ch[dir];
        }
    }
}
int get(int now,int dir){//now是点的编号,dir==0找前驱,dir==1找后继,此处是要找的now已经是根的情况下
    //if(T[now].num>1) return T[now].val;//决定是否为找严格小于大于的前驱和后继(注释掉就是严格
    now=T[now].ch[dir];
    if(!now) return -1;
    while(T[now].ch[dir^1]) now=T[now].ch[dir^1];
    return T[now].val;
}
int find(int x){//找到key为x的编号
    int now=root;
    while(1){
        if(T[now].val==x){ splay(now,root);return now;}
        int dir=x<T[now].val?0:1;
        if(!T[now].ch[dir]) return 0;
        now=T[now].ch[dir];
    }
}
void dele(int x){//删掉key为x的点
    x=find(x);//注释掉这句就是删除编号为x的点啦
    if(!x) return;
    //if(T[x].num>1){T[x].num--;T[x].sum--;return ;}//决定是删掉该权值的一个点还是删掉权值的所有点
    if(lchild(x)){
        int pre=lchild(x);
        while(rchild(pre)) pre=rchild(pre);
        splay(pre,lchild(root));
        link(rchild(root),pre,1);
        link(pre,0,1);
        update(pre);
    }else{
        root=rchild(x);
        T[root].fa=0;
    }
}
int kth(int k){//key第k大的点(key)的编号,不去重的哦,去重的话sz对每种key只记1即可
    int now=root;
    while(1){
        int used=T[now].sz-T[rchild(now)].sz;
        if(T[lchild(now)].sz<k&&k<=used){ splay(now,root);return now;}
        if(k<used) now=lchild(now);
        else now=rchild(now),k-=used;
    }
}
int op,x,p,now;
int main(){
    while(scanf("%d",&op)&&op){
        if(op==1){
            scanf("%d%d",&x,&p);
            insert(p,x);//插入p,id为x
        }else if(!root) printf("0\n");//树空输出0
        else{
            if(op==2){
                now=kth(T[root].sz);
                printf("%d\n",T[now].num);
            }
            else{
                now=kth(1);
                printf("%d\n",T[now].num);
            }
            dele(T[now].val);
        }
    }
    return 0;
}

对模板的整理:

查询数x的排名:

int rak(int val){//查询key==val的数的排名
    int pos=find(val);
    return T[lchild(pos)].sz+1;
}

每次插入一个点:

inline int newnode(int u,int fa){
    T[++cnt].fa=fa;T[cnt].val=u;
    T[cnt].sz=T[cnt].num=1;//涉及sum&num
    return cnt;
}
void update(int x){T[x].sz=T[lchild(x)].sz+T[rchild(x)].sz+T[x].num;}//涉及sum&num
void insert(int x,int idx){//权重为x的,插入idx个
    if(!root) root=newnode(x,0,idx);
    else{
        int now=root;
        while(1){
            T[now].sz++;//涉及sum&num
            if(T[now].val==x){ T[now].num++,splay(now,root);return;}//涉及sum&num
            int dir=T[now].val<x?1:0;
            if(!T[now].ch[dir]){
                splay(T[now].ch[dir]=newnode(x,now,idx),root);
                return;
            }
            now=T[now].ch[dir];
        }
    }
}

同时插入多个权值相同的点,num记录点的个数,sum为所有点的个数:

inline int newnode(int u,int fa,int num){
    T[++cnt].fa=fa;T[cnt].val=u;
    T[cnt].sz=T[cnt].num=num;//涉及sum&num
    return cnt;
}
void update(int x){T[x].sz=T[lchild(x)].sz+T[rchild(x)].sz+T[x].num;}//涉及sum&num
void insert(int x,int idx){//权重为x的,插入idx个
    if(!root) root=newnode(x,0,idx);
    else{
        int now=root;
        while(1){
            T[now].sz+=idx;//涉及sum&num
            if(T[now].val==x){ T[now].num+=idx,splay(now,root);return;}//涉及sum&num
            int dir=T[now].val<x?1:0;
            if(!T[now].ch[dir]){
                splay(T[now].ch[dir]=newnode(x,now,idx),root);
                return;
            }
            now=T[now].ch[dir];
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值