2025-01-21 20:09阅读: 4评论: 0推荐: 0

P1486 [NOI2004] 郁闷的出纳员

P1486 [NOI2004] 郁闷的出纳员

题目翻译:

维护一个可重数集,共有 \(n\) 次操作,和一个最小限制 \(min\),共有四种操作:

  • \(I\) \(k\) 给集合添加 \(k\)\(k<min\) 则直接删除(不算入删除个数)
  • \(A\) \(k\) 将集合中的所有元素加上 \(k\)
  • \(S\) \(k\) 将所有元素减少 \(k\) 并将所有值小于 \(min\) 的删除
  • \(F\) \(k\) 输出集合中元素从大到小的第 \(k\) 名的值,若 \(k\) 大于当前元素的值,则输出 \(-1\)

最后还要输出总共删除了多少元素

思路:

对于求第 \(k\) 大的数和随时可以插入等操作,很容易想到用平衡树来维护。由于可能会有重复的数,我们可以给每个节点都维护一个 \(cnt\) 用来储存该节点数的个数。

  • 对于插入操作就与普通的插入没有区别,只要特判一下,看是否有一样的,有就直接给该节点加一。并且要特判一下是否小于 \(min\),若小于则直接跳过。
void ins(int key){
    if(key<mi)return;
    int now=rt,f=0;
    while(now && tr[now].key!=key){
        f=now;
        now=tr[now].s[key>tr[now].key];
    }
    if(now){
        tr[now].cnt++;
        splay(now); 
        return;
    }
    now=newnode(key);
    fa(now)=f;
    if(f)tr[f].s[key>tr[f].key]=now;
    splay(now);
}
  • 对于给所有加上 \(k\)我们可以暴力的给所有节点都加上 \(k\),不管是否已经删除或者有没有用,应为删除的节点已经没有与树有连边,所以无法访问,自然没有影响,时间复杂度为 \(O(n)\)
for(int i=1;i<=idx;i++){
    tr[i].key+=k;
}
  • 对于对所有节点减去 \(k\),我们也可以全部减去,但为了删点,我们可以先找到最接近且 \(\geq k+min\) 的点,将他给 \(splay\) 到根节点,这样所有减去 \(k\) 会小于 \(min\) 的节点都会在根节点的左子树,这样只需要给 \(ans\) 加上子树大小,然后断开连接即可。最后给所有节点减去 \(k\).
void find(int key){
    int now=rt;
    while(key!=tr[now].key && tr[now].s[key>tr[now].key]){
        now=tr[now].s[key>tr[now].key];
    }
    splay(now);
}
int nxt(int key){
    find(key);
    if(tr[rt].key>=key)return rt;
    int now=rc(rt);
    while(lc(now))now=lc(now);
    return now;
}
void del2(int x){
    int now=nxt(x+mi);
    splay(now);
    ans+=tr[lc(now)].siz;
    lc(now)=0;
    pushup(now);
    for(int i=1;i<=idx;i++){
        tr[i].key-=x;
    }
}
  • 查找第 \(k\) 大的数。与普通查找没有什么区别但要注意的是,由于为了防止树中没有符合要求的节点,所以会先加入一个极大值,所以找的时候要找第 \(k+1\) 大的数。其次,由于有重复的数,所以在转移时还会要算上 \(cnt\)
int kth(int rk){
    int now=rt;
    if(rk>=tr[now].siz)return -1;
    rk++;
    while(true){
        int sz=tr[rc(now)].siz;
        if(sz>=rk && rc(now)){
            now=rc(now);
        }
        else if(rk>tr[rc(now)].siz+tr[now].cnt){
            rk-=sz+tr[now].cnt;
            now=lc(now);
        }
        else{
            return tr[now].key;
        }
    }
}

完整代码:

#include<bits/stdc++.h>
#define lc(x) tr[(x)].s[0]
#define rc(x) tr[(x)].s[1]
#define fa(x) tr[(x)].fa
using namespace std;
const int N=3e5+10;
struct tree{
    int s[2],fa,siz,key,cnt;
    tree(){s[0]=s[1]=fa=siz=cnt=key=0;}
}tr[N];
int idx,rt,mi,ans;
void pushup(int x){
    tr[x].siz=tr[lc(x)].siz+tr[rc(x)].siz+tr[x].cnt;
}
void clear(int x){
    lc(x)=rc(x)=fa(x)=tr[x].siz=tr[x].key=tr[x].cnt=0;
}
int newnode(int key){
    tr[++idx].key=key;
    tr[idx].siz=1;
    tr[idx].cnt=1;
    return idx;
}
int get(int x){
    return x==rc(fa(x));
}
void rotate(int x){
    int y=fa(x),z=fa(y),c=get(x);
    if(tr[x].s[c^1])fa(tr[x].s[c^1])=y;
    tr[y].s[c]=tr[x].s[c^1];
    tr[x].s[c^1]=y;
    fa(y)=x;
    fa(x)=z;
    if(z)tr[z].s[y==rc(z)]=x;
    pushup(y);
    pushup(x);
}
void splay(int x){
    for(int f=fa(x);f=fa(x),f;rotate(x)){
        if(fa(f))rotate(get(x)==get(f)?f:x);
    }
    rt=x;
}
void ins(int key){
    if(key<mi)return;
    int now=rt,f=0;
    while(now && tr[now].key!=key){
        f=now;
        now=tr[now].s[key>tr[now].key];
    }
    if(now){
        tr[now].cnt++;
        splay(now); 
        return;
    }
    now=newnode(key);
    fa(now)=f;
    if(f)tr[f].s[key>tr[f].key]=now;
    splay(now);
}
int kth(int rk){
    int now=rt;
    if(rk>=tr[now].siz)return -1;
    rk++;
    while(true){
        int sz=tr[rc(now)].siz;
        if(sz>=rk && rc(now)){
            now=rc(now);
        }
        else if(rk>tr[rc(now)].siz+tr[now].cnt){
            rk-=sz+tr[now].cnt;
            now=lc(now);
        }
        else{
            return tr[now].key;
        }
    }
}
void find(int key){
    int now=rt;
    while(key!=tr[now].key && tr[now].s[key>tr[now].key]){
        now=tr[now].s[key>tr[now].key];
    }
    splay(now);
}
int nxt(int key){
    find(key);
    if(tr[rt].key>=key)return rt;
    int now=rc(rt);
    while(lc(now))now=lc(now);
    return now;
}
void del2(int x){
    int now=nxt(x+mi);
    splay(now);
    ans+=tr[lc(now)].siz;
    lc(now)=0;
    pushup(now);
    for(int i=1;i<=idx;i++){
        tr[i].key-=x;
    }
}
int main(){
    int n;
    scanf("%d%d",&n,&mi);
    ins(1547483647);
    for(int i=1;i<=n;i++){
        char op;
        int k;
        cin>>op>>k;
        if(op=='I'){
            ins(k);
        }
        if(op=='A'){
            for(int i=1;i<=idx;i++){
                tr[i].key+=k;
            }
        }
        if(op=='S'){
            del2(k);
        }
        if(op=='F'){
            printf("%d\n",kth(k));
        }
    }
    printf("%d\n",ans);
}

平衡树讲解

本文作者:XichenOC

本文链接:https://www.cnblogs.com/XichenOC/p/18684363

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   XichenOC  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起