LG P4278 带插入区间K小值

Description

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

Solution

区间K小值可以用在值域上分块解决

先用序列分块找到左右端点所在的块,如果在同一个块内,用桶统计每个值出现次数和每个值域块内的数出现次数,先找到第K小在那个块内,再在块内查找,类似BSGS

如果不在一个块内,就处理两端零散块,在中间的每个块上维护每个数出现的次数和每个块内出现的次数

修改时维护每个块内的信息

插入时维护块状链表

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<list>
using namespace std;
int n,B=300,L[305],R[305],bel[70005],sum[70500][305],sumblo[305][305],m,las,tot[70005],totblo[305];
char opt[3];
queue<int>q;
list<int>lst[305];
inline int read(){
    int w=0,f=1;
    char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
    return w*f;
}
void solve(int k,int p,int len){
    list<int>::iterator it=lst[k].begin();
    for(;p;p--)++it;
    for(;len;len--)q.push(*it),++tot[*it],++totblo[bel[*it]],++it;
}
int main(){
    n=read();
    for(int i=1;i<=70001;i++)bel[i]=(i-1)/B+1;
    for(int i=1;i<=300;i++)L[i]=R[i-1]+1,R[i]=i*B;
    for(int i=1;i<=n;i++){
        int x=read()+1;
        lst[bel[i]].push_back(x);
        for(int j=bel[i];j<=300;j++)++sum[x][j],++sumblo[bel[x]][j];
    }
    m=read();
    for(;m;m--){
        scanf("%s",opt);
        if(opt[0]=='Q'){
            int l=read()^las,r=read()^las,K=read()^las;
            if(bel[l]==bel[r]){
                solve(bel[l],l-L[bel[l]],r-l+1);
                for(int i=1;;i++)
                    if(K>totblo[i])K-=totblo[i];
                    else{
                        for(int j=L[i];;j++)
                            if(K>tot[j])K-=tot[j];
                            else{las=j;break;}
                        break;
                    }
            }
            else{
                solve(bel[l],l-L[bel[l]],R[bel[l]]-l+1),solve(bel[r],0,r-L[bel[r]]+1);
                for(int i=1;;i++)
                    if(K>totblo[i]+sumblo[i][bel[r]-1]-sumblo[i][bel[l]])K-=totblo[i]+sumblo[i][bel[r]-1]-sumblo[i][bel[l]];
                    else{
                        for(int j=L[i];;j++)
                            if(K>tot[j]+sum[j][bel[r]-1]-sum[j][bel[l]])K-=tot[j]+sum[j][bel[r]-1]-sum[j][bel[l]];
                            else{las=j;break;}
                        break;
                    }
            }
            printf("%d
",--las);
            while(q.size())--tot[q.front()],--totblo[bel[q.front()]],q.pop();
        }
        else if(opt[0]=='M'){
            int x=read()^las,v=(read()^las)+1,temp=0;
            list<int>::iterator it=lst[bel[x]].begin();
            for(int i=x-L[bel[x]];i;i--)++it;
            temp=*it,*it=v;
            for(int i=bel[x];i<=300;i++)--sum[temp][i],--sumblo[bel[temp]][i],++sum[v][i],++sumblo[bel[v]][i];
        }
        else{
            int x=read()^las,v=(read()^las)+1;
            list<int>::iterator it=lst[bel[x]].begin();
            for(int i=x-L[bel[x]];i;--i)++it;
            lst[bel[x]].insert(it,v);
            for(int i=bel[x];i<=300;i++)++sum[v][i],++sumblo[bel[v]][i];
            for(int i=bel[x]+1;i<=300;i++)
                if(lst[i-1].size()>300){
                    int temp=lst[i-1].back();
                    --sum[temp][i-1],--sumblo[bel[temp]][i-1];
                    lst[i-1].pop_back(),lst[i].push_front(temp);
                }
                else break;
        }
    }
    return 0;
}
带插入区间K小值
原文地址:https://www.cnblogs.com/JDFZ-ZZ/p/14393644.html