[BZOJ4923]K小值查询

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ4923]K小值查询相关的知识,希望对你有一定的参考价值。

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4923

好题啊!直接做肯定是不行的,我们需要发现一些性质。考虑减去k后对各个数的影响,对于(k,2k](即大于k小于等于2k)的数,它们被减后会小于等于k,但对于>2k的数,减去k后还是大于k的,且那些数的相对大小不变,因为要动态维护第k大,我们可以想到用平衡树,一般用splay,然后对于(k,2k]的数,直接暴力提取,一个个减去k,再暴力插入回去,大于2k的数直接打个标记就好了。我们分析下复杂度,(k,2k]的数至少会减少一半,那么对于每个数,减的次数,也就是插入的次数,最多是logn,总复杂度为nlognlogn

贴代码:

技术分享
#include<cstdio>
#include<algorithm>
using namespace std;
//-----------------------------------------------head-------------------------------------------//
const int N=100010;
typedef long long LL;
const LL inf=1e18;
int n,q,fa[N],c[N][2],siz[N],lazy[N],rt,s[N],top;
LL a[N];
int Write[20];
inline int read() {int d=0,f=1; char c=getchar(); while (c<0||c>9) {if (c==-) f=-1; c=getchar();} while (c>=0&&c<=9) d=(d<<3)+(d<<1)+c-48,c=getchar(); return d*f;}
inline void write(int x){int t=0; if (x<0) putchar(-),x=-x; for (;x;x/=10) Write[++t]=x%10; if (!t) putchar(0); for (int i=t;i>=1;i--) putchar((char)(Write[i]+48));}
inline void pushup(int x){siz[x]=siz[c[x][0]]+siz[c[x][1]]+1;}
inline void mark(int x,int v){if (!x) return;lazy[x]+=v;a[x]+=v;}
inline void pushdown(int x){if (!lazy[x]) return; mark(c[x][0],lazy[x]);mark(c[x][1],lazy[x]);lazy[x]=0;}
inline void build(int &k,int l,int r,int last)
{
    if (l>r) return; k=(l+r)>>1; fa[k]=last;
    build(c[k][0],l,k-1,k); build(c[k][1],k+1,r,k);
    pushup(k);
}
inline void rotate(int x,int &k)
{
    int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
    if (y==k) k=x; else c[z][c[z][1]==y]=x;
    fa[c[x][r]]=y; fa[y]=x; fa[x]=z;
    c[y][l]=c[x][r]; c[x][r]=y;
    pushup(y); pushup(x);
}
inline void splay(int x,int &k)
{
    while (x!=k)
    {
        int y=fa[x],z=fa[y];
        if (y!=k) if (c[y][0]==x^c[z][0]==y) rotate(x,k); else rotate(y,k);
        rotate(x,k);
    }
}
inline int find(int x,int k)
{
    while (x)
    {
        pushdown(x);
        if (siz[c[x][0]]+1==k) return x;
        if (siz[c[x][0]]+1>k) x=c[x][0]; else k-=siz[c[x][0]]+1,x=c[x][1];
    }
}
inline int getpre(int x,int k)
{
    int res;
    while (x)
    {
        pushdown(x);
        if (a[x]<=k) res=x,x=c[x][1]; else x=c[x][0];
    }
    return res;
}
inline int getnxt(int x,int k)
{
    int res;
    while (x)
    {
        pushdown(x);
        if (a[x]>k) res=x,x=c[x][0]; else x=c[x][1];
    }
    return res;
}
inline void dfs(int x)
{
    if (!x) return;
    pushdown(x);
    dfs(c[x][0]);
    s[++top]=x;
    dfs(c[x][1]);
}
inline void insert(int &x,int k,int last)
{
    if (!x) {x=k;c[x][0]=c[x][1]=0;fa[x]=last;siz[x]=1;return;}
    pushdown(x);
    insert((a[x]>=a[k])?c[x][0]:c[x][1],k,x);
    pushup(x);
}
int main()
{
    n=read(); q=read();
    for (int i=2;i<=n+1;i++) a[i]=read();
    sort(a+2,a+2+n); a[1]=-inf; a[n+2]=inf;
    build(rt,1,n+2,0);
    while (q--)
    {
        int op=read(),k=read();
        if (op==1)
        {
            k=find(rt,k+1);
            splay(k,rt);
            write(a[k]); puts("");
        }
        else
        {
            int x=getpre(rt,k),y=getnxt(rt,k<<1);
            splay(x,rt); splay(y,c[x][1]);
            int z=c[y][0];
            if (z) top=0,dfs(z),fa[z]=c[y][0]=0;
            pushup(y); pushup(x); mark(y,-k);
            if (z) for (int i=1;i<=top;i++) a[s[i]]-=k,insert(rt,s[i],0),splay(s[i],rt);
        }
    }
    return 0;
}
View Code

以上是关于[BZOJ4923]K小值查询的主要内容,如果未能解决你的问题,请参考以下文章

4923: [Lydsy1706月赛]K小值查询 平衡树 非旋转Treap

BZOJ3065 带插入区间K小值

[BZOJ3065]带插入区间K小值

Bzoj3065 带插入区间K小值

[bzoj3065] 带插入区间第k小值 [重量平衡树套线段树]

BZOJ3065 带插入区间K小值