[BZOJ]3065: 带插入区间K小值 块状链表

Description
从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i]。跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴。这时跳蚤国王决定理性愉悦一下,查询区间k小值。他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少。
这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问。
这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少。
这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问。(orz 主席树)
这时伏特发现有些迟到的跳蚤会插入到这一行的某个位置上,他感到非常生气,因为……他不会做了。
请你帮一帮伏特吧。
快捷版题意:带插入、修改的区间k小值在线查询。

题解:

分块。维护每块的原数组与排序后数组,当某块的size刚好达到两个块的大小时,分裂成两块,其它都是普通分块的套路了。如果要对拍,记得在对拍完之后加上强制在线,别问我是怎么知道的……

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int block_size=600;
const int Maxn=70010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,num,next[120],b[120][1210],a[120][1210],size[120];
//a为原数组 b为排序后数组 num为块数 next[i]为第i块的下一块序号 
void rebuild(int x)//重构第x块 
{
    for(int i=1;i<=size[x];i++)b[x][i]=a[x][i];
    sort(b[x]+1,b[x]+1+size[x]);
}
int binary(int x,int v)//第x块有多少数比v小 
{
    int l=1,r=size[x];
    while(l<=r)
    {
        int mid=l+r>>1;
        if(b[x][mid]<v)l=mid+1;
        else r=mid-1;
    }
    return l-1;
}
int t1,t2;
void get(int x)//t1表示x所属块的序号 t2表示x是块中的第几个 
{
    t1=1;t2=x;
    while(t2>=size[t1])
    {
        if(t2==size[t1])break;
        if(next[t1]==-1)break;
        t2-=size[t1];
        t1=next[t1];
    }
}
int query(int L,int R,int k)
{
    int bl,br,tl,tr;
    get(L);bl=t1;tl=t2;
    get(R);br=t1;tr=t2;
    int l=0,r=70000;
    while(l<=r)
    {
        int mid=l+r>>1,tot=0;
        if(bl==br)
        {
            for(int i=tl;i<=tr;i++)
            tot+=(a[bl][i]<mid);
        }
        else
        {
            for(int i=tl;i<=size[bl];i++)tot+=(a[bl][i]<mid);
            for(int i=1;i<=tr;i++)tot+=(a[br][i]<mid);
            int t=next[bl];
            while(t!=br)
            {
                tot+=binary(t,mid);
                t=next[t];
            }
        }
        if(tot<=k-1)l=mid+1;
        else r=mid-1;
    }
    return l-1;
}
void modify(int x,int v)
{
    get(x);int t=t1;x=t2;
    int i;
    for(i=1;i<=size[t];i++)
    if(a[t][x]==b[t][i])break;
    a[t][x]=b[t][i]=v;
    while(i<size[t]&&b[t][i]>b[t][i+1])swap(b[t][i],b[t][i+1]),i++;
    while(i>1&&b[t][i]<b[t][i-1])swap(b[t][i],b[t][i-1]),i--;
}
void insert(int x,int v)
{
    get(x);int t=t1;x=t2;
    for(int i=size[t]+1;i>x;i--)a[t][i]=a[t][i-1];
    a[t][x]=b[t][++size[t]]=v;
    if(size[t]==2*block_size)
    {
        next[++num]=next[t];next[t]=num;
        size[t]=size[num]=block_size;
        for(int i=1;i<=block_size;i++)a[num][i]=a[t][i+block_size];
        rebuild(num);rebuild(t);
    }
    else
    {
        int i=size[t];
        while(i>1&&b[t][i]<b[t][i-1])swap(b[t][i],b[t][i-1]),i--;
    }
}
int main()
{
    memset(next,-1,sizeof(next));
    n=read();num=(n-1)/block_size+1;
    for(int i=1;i<=n;i++)
    {
        int t1=(i-1)/block_size+1,t2=(i-1)%block_size+1;
        a[t1][t2]=b[t1][t2]=read();
        size[t1]++;
    }
    for(int i=1;i<=num;i++)
    {
        rebuild(i);
        if(i!=num)next[i]=i+1;
    }
    int zaixian=1;
    int ans=0,Q=read();
    while(Q--)
    {
        char str[3];
        scanf("%s",str);
        int x=read()^(ans*zaixian),y=read()^(ans*zaixian);
        if(str[0]=='Q')ans=query(x,y,read()^(ans*zaixian)),printf("%d\n",ans);
        else if(str[0]=='M')modify(x,y);
        else insert(x,y);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值