4923: [Lydsy1706月赛]K小值查询(Splay)

Description

维护一个长度为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小的值。


题解:
很容易想到,1 ~ k中的数是不会被修改的,k ~ 2k的数修改后会和前面1 ~ k的数的排名混合在一起,而2*k以后的数在操作后是不会改变相对排名的

所以每次对于2操作,暴力修改k ~ 2k中的所有数,将其重新插入即可


AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+10;
int n,m,op,k,root,tot,a[MAXN];
struct node{ int son[2],fa,val,sz,mx,lazy; }t[MAXN];
inline int id(int x){ return x==t[t[x].fa].son[1]; }
inline void pushup(int x){
    t[x].sz=t[t[x].son[0]].sz+t[t[x].son[1]].sz+1;
    t[x].mx=max(t[x].val,max(t[t[x].son[0]].mx,t[t[x].son[1]].mx));
}
inline void update(int x,int k){
    if(t[x].son[k]){
        t[t[x].son[k]].val += t[x].lazy;
        t[t[x].son[k]].lazy += t[x].lazy;
        t[t[x].son[k]].mx += t[x].lazy;
    }
}
inline void pushdown(int x){
    if(t[x].lazy){
        update(x,0),update(x,1);
        t[x].lazy=0;
    }
}
void push(int x){//下放标记
    if(t[x].fa) push(t[x].fa);
    pushdown(x);
}
inline void rotate(int x){
    int y=t[x].fa,z=t[y].fa,k=id(x);
    t[z].son[id(y)]=x; t[x].fa=z;
    t[y].son[k]=t[x].son[k^1]; t[t[x].son[k^1]].fa=y;
    t[x].son[k^1]=y; t[y].fa=x;
    pushup(y); pushup(x);
}
int build(int fa,int l,int r){
    if(l>r) return 0;
    int mid=l+r>>1;
    t[mid].fa=fa; t[mid].val=a[mid];
    t[mid].son[0]=build(mid,l,mid-1);
    t[mid].son[1]=build(mid,mid+1,r);
    pushup(mid); return mid;
}
inline void splay(int x,int pos){
    push(x);
    while(t[x].fa!=pos){
        int y=t[x].fa,z=t[y].fa;
        if(z!=pos) id(x)==id(y) ? rotate(y):rotate(x);
        rotate(x);
    }
    if(!pos) root=x;
}
inline int kth(int x){
    int u=root;
    while(1){
        pushdown(u);
        if(t[t[u].son[0]].sz >=x) u=t[u].son[0];
        else{
            x -= t[t[u].son[0]].sz+1;
            if(!x) { splay(u,0); return t[u].val; }
            u=t[u].son[1];
        }
    }
}
inline int Find(int x){//找到第一个大于x的数
    int u=root;
    while(1){
        pushdown(u);
        if(t[t[u].son[0]].mx>x) u=t[u].son[0];
        else{
            if(t[u].val>x) { splay(u,0); return u; }
            u=t[u].son[1];
        }
    }
}
inline int Pre(int x){
    splay(x,0);
    x = t[x].son[0];
    while(t[x].son[1]) x=t[x].son[1];
    return x;
}
inline void Insert(int x){
    int u=root,fa=0;
    while(1){
        pushdown(u);
        if(!u){
            t[x].fa=fa; t[fa].son[t[x].val>t[fa].val]=x;
            t[x].mx=t[x].val; t[x].sz=1;
            splay(x,0); return;
        }
        fa=u; u=t[u].son[t[x].val>t[u].val];
    }
}
void dfs(int x,int k){//遍历子树,重新插入减去k后的值
    if(!x) return;
    pushdown(x);
    dfs(t[x].son[0],k); dfs(t[x].son[1],k);
    t[x].fa=t[x].son[0]=t[x].son[1]=t[x].sz=t[x].mx=0;
    t[x].val -= k; Insert(x);
}
inline void change(int k){
    int x=Pre(Find(k)),y=Find(2*k);
    splay(x,0); splay(y,x);//y的左子树中全是k~2*k的数
    int s=t[y].son[0]; t[y].son[0]=t[t[y].son[0]].fa=0;
    dfs(s,k);//更新这部分,重新插入值
    splay(y,0);//值大于2*k的部分添加标记
    x=Pre(Find(2*k)),y=n+2;
    splay(x,0); splay(y,x);
    if(!t[y].son[0]) return;
    t[t[y].son[0]].lazy -= k;//添加标记
    t[t[y].son[0]].val -= k;
    t[t[y].son[0]].mx -= k;
}
int main(){
    //freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=2;i<=n+1;i++) scanf("%d",&a[i]);
    a[1]=-2e9; a[n+2]=2e9;
    sort(a+1,a+n+2+1);
    root=build(0,1,n+2);
    while(m--){
        scanf("%d%d",&op,&k);
        if(op==1) printf("%d\n",kth(k+1));
        else change(k);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值