• [BZ4923][Lydsy1706月赛]K小值查询


    K小值查询

    题面

    维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作:

    1 k,将序列a从小到大排序,输出a_k的值。

    2 k,将所有严格大于k的数a_i减去k。

    Input

    第一行包含两个正整数n,m(1<=n,m<=100000),分别表示序列的长度和操作的个数。

    第二行包含n个正整数a_1,a_2,...,a_n(1<=a_i<=10^9),分别表示序列中的每个元素。

    接下来m行,每行两个正整数op(1<=op<=2),k,若op=1,则1<=k<=n;若op=2,则1<=k<=10^9;依次描述每个操作。

    Output

    输出若干行,对于每个询问输出一行一个整数,即第k小的值。

    Sample Input

    4 5
    1 5 6 12
    2 5
    1 1
    1 2
    1 3
    1 4

    Sample Output

    1
    1
    5
    7

    思路

    乍一看是一道平衡树上的区间修改。然而发现操作1是排名操作。显然,区间修改的splay是以编号作为关键字维护的,无法排名。那我们就思考如何用权值关键字的splay解决问题。

    我们能发现:所有严格大于k的节点减去k之后相对大小都不会改变。而一颗子平衡树是按照内部元素大小关系排序的。所以我们推出:对于所有节点权值都严格大于k的子树,全部减k后树的形状不会改变。

    我们可以先把所有点根据这个特性分成三个区间:((-inf,k],(k,2k],(2k,+inf))。我们容易发现,每次操作后两个区间所有数都会减去k,操作后第二个区间和第一个区间会合并。而第三个区间一部分会成为新的2区间,一部分会留在3区间。

    我们考虑修改操作,先把k的后继的前驱(注意,这个点不是k,因为k可能不在树里)转到root,再把2k的后继转到k的右儿子。然后把2k这个节点的左子树从主树上拆下来,则得到代表区间((k,2k])的子树。然后把这些节点拆散,暴力的把每个节点的val减去k,然后再一个一个暴力插入。因为所有数在区间2最多存在一次,所以这一步复杂度为(O(n space logspace n))。第三个区间则将2k节点,打个tag即可。

    查询操作还是常规的查询操作,只是每次递归到一个点后先执行Push_Down操作,即把tag下推。

    注意事项

    代码实现请注意:

    1. 删除任意一个非根节点之后,一定要更新他所有祖先的信息。
    2. 在向下遍历到一个新的节点/对子节点进行操作时,尽量随手Push_Down。(反正splay常数已经够大了QAQ)
    3. splay之前,请从根节点到splay点路径上的所有点Push_Down。(因为tag在旋转时会乱掉,而与size不同,tag无法在操作后用Push_Up操作从子节点还原父节点的tag

    代码

    /**************************************************************
        Problem: 4923
        User: GavinZheng
        Language: C++
        Result: Accepted
        Time:9612 ms
        Memory:5236 kb
    ****************************************************************/
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    #define inf (ll)(1e18+1000)
    #define maxn (int)(1e5+1000)
    using namespace std;
    int idx,n,m,root,beh;
    ll ma[maxn],val[maxn];
    int tag[maxn],size[maxn],cnt[maxn],fa[maxn],son[maxn][2];
    void push_down(int x){
        if(!x)return;
        if(son[x][0]){
            val[son[x][0]]+=tag[x];
            tag[son[x][0]]+=tag[x];
            ma[son[x][0]]+=tag[x];
        }
        if(son[x][1]){
            val[son[x][1]]+=tag[x];
            tag[son[x][1]]+=tag[x];
            ma[son[x][1]]+=tag[x];
        }
        tag[x]=0;
        return;
    }
    void Down(int x){
        if(!x)return;
        Down(fa[x]);
        push_down(x);
    }
    void push_up(int x){
        size[x]=cnt[x];ma[x]=val[x];
        if(son[x][0]){
            size[x]+=size[son[x][0]];
            ma[x]=max(ma[x],ma[son[x][0]]);
        }
        if(son[x][1]){
            size[x]+=size[son[x][1]];
            ma[x]=max(ma[x],ma[son[x][1]]);
        }
        return;
    }
    void rotate(int x){
        int y=fa[x],z=fa[y],o=(son[y][1]==x);
        son[y][o]=son[x][o^1];
        fa[son[x][o^1]]=y;
     
        son[x][o^1]=y;
        fa[y]=x;
     
        son[z][son[z][1]==y]=x;
        fa[x]=z;
        push_up(y);
        push_up(x);
        return;
    }
    void splay(int x,int v=0){//If x's father is v,stop rotating.
        if(!x)return;
        Down(x);
        for(int y;(fa[x]&&fa[x]!=v);rotate(x)){
            y=fa[x];
            if(fa[y]==v)continue;
     
            rotate((son[fa[y]][0]==y)==(son[fa[x]][0]==x)?y:x);
        }
        if(!v)root=x;
    }
    void insert(int x,ll a,int cntx=1,int idxx=0){
        int y=0;
        while(x&&a!=val[x]){
            push_down(x);
            y=x;
            x=son[x][a>val[x]];
        }
        if(x){
            cnt[x]+=cntx;splay(x);
        }
        else{
            x=(idxx?idxx:++idx);
            val[x]=a;
            fa[x]=y;
            son[y][a>val[y]]=x;
            cnt[x]=cntx;
                tag[x]=0;
            son[x][0]=son[x][1]=0;
            push_up(x);splay(x);
        }
         
        return;
    }
    int kth(int x,int k){
        push_down(x);
        if(size[son[x][0]]>=k) return kth(son[x][0],k);
        if(size[son[x][0]]+cnt[x]>=k) return x;
        else return kth(son[x][1],k-size[son[x][0]]-cnt[x]);
    }
    void suc(int x,int a){
        while(x){
            push_down(x);
            if(val[x]>a){
                beh=x;
                x=son[x][0];
            }
            else{
                x=son[x][1];
            }
        }
        return;
    }
    void reinsert(int x,int k){
        if(!x)return;
        push_down(x);
        if(son[x][0])reinsert(son[x][0],k);
        if(son[x][1])reinsert(son[x][1],k);
        insert(root,val[x]-k,cnt[x],x);
        return;
    }
    void change(int k){
        suc(root,2*k);
        int k2_mark=beh;
        suc(root,k);
        int k_mark=beh;
        splay(k2_mark);
        val[root]-=k;val[son[root][1]]-=k;ma[son[root][1]]-=k;tag[son[root][1]]-=k;
        push_down(son[root][1]);
        push_up(root);
        splay(k_mark);
        splay(kth(root,size[son[k_mark][0]]));//put the previous of k on the root_position
        //if k presense in the tree,put k instead.
        splay(k2_mark,root);
        push_down(root);push_down(son[root][1]);
        int head=son[son[root][1]][0];son[son[root][1]][0]=0;fa[head]=0;
        push_up(son[root][1]);push_up(root);reinsert(head,k);
    }
    int main(){
        //freopen("in","r",stdin);
        scanf("%d%d",&n,&m);
        insert(root,inf);insert(root,-inf);
        for(int i=1;i<=n;i++){
            int a;scanf("%d",&a);insert(root,a);
        }
        for(int i=1;i<=m;i++){
            int type,num;scanf("%d%d",&type,&num);
            if(type==1) printf("%lld
    ",val[kth(root,num+1)]);
            else change(num);
        }
    }
    
  • 相关阅读:
    Linux学习33 crontab定时任务语法在线校验 上海
    python测试开发django175.bootstrap导航带下拉菜单的标签页标签页(navtabs) 上海
    python测试开发django172.jQuery 发送请求获取的数据设置为全局变量 上海
    team讨论有感
    蜕变(3)---模式
    uml建模的随想
    Bridge Strategy 和State的区别
    友元在模式中的运用
    Design&Pattern团队《设计模式在软件开发的应用》精华版
    面向对象乱弹(一)
  • 原文地址:https://www.cnblogs.com/GavinZheng/p/10773665.html
Copyright © 2020-2023  润新知