带插入区间K小值
题解
很明显的块状链表做法。分块万岁
我们先对值域进行分块,只要知道每个块中每个数的出现次数,很容易就可以在的时间内得到第k小。
然后我们需要维护一个序列,进行序列分块。
询问时,我们需求出一段区间内每个数,每个块的出现次数。这可以通过前缀和快速的求出,之前统计下就可以了。对于每个值域块与数字的出现次数。查询时两端暴力处理,中间前缀和直接差分。这样预处理,查询。
修改时也只需要暴力更新块中前缀的信息,就可以解决。
由于有插入信息的存在,需要用块状链表来动态维护,也是可以解决的。
我们每次在一个块中插入一个数时,这个块都会多出一个数,我们可以将这个多的数插到下一个块中。而插入的这个数又只会影响这个块中的信息的开头,我们之后会暴力维护,我们更新时也只需更新插入块中的前缀。这更新也只用的时间。
所以,整个过程的时间复杂度是的,可以过去。
源码
#include<cstdio>
#include<iostream>
#include<list>
using namespace std;
#define MAXN 300
#define MAXM 70500
typedef long long LL;
typedef list<int>::iterator iter;
char buf[(int)3e7],*ss=buf;
inline int init(){buf[fread(buf,1,(int)3e7-1,stdin)]='\n';fclose(stdin);return 0;}
const int __START__=init();
inline int read(){
int d=0;
while(!isdigit(*ss))++ss;
while(isdigit(*ss))d=d*10+(*ss++^'0');
return d;
}
#define bel(x) ((x-1)/siz+1)
const int blocks=235;
const int siz=300;
int n,m,ans,q,L[MAXN],R[MAXN];
int pre[MAXM][MAXN],prb[MAXN][MAXN];
int tot[MAXM],tob[MAXN];
int stak,sta[1005];
list<int> s[MAXN];
int modify(list<int>&l,int x,int v){
iter it=l.begin();
while(x--)++it;
const int ret=*it;*it=v;
return ret;
}
void get(list<int>&l,int x,int s){
iter it=l.begin();
while(x--)++it;
while(s--)++tot[sta[++stak]=*it],++tob[bel(*it)],++it;
}
signed main(){
n=read();
for(int i=1;i<=blocks;i++)L[i]=R[i-1]+1,R[i]=i*siz;
for(int i=1;i<=n;i++){
int x=read()+1;
const int id=bel(i);s[id].push_back(x);
int *P=pre[x],*B=prb[bel(x)];
for(int j=id;j<=blocks;j++)++P[j],++B[j];
}
for(m=read()^ans;m--;){
while(isspace(*ss))++ss;
switch(*ss){
case'Q':{
int l=read()^ans,r=read()^ans,k=read()^ans;
stak=0;const int bl=bel(l),br=bel(r);
if(bl==br){
get(s[bl],l-L[bl],r-l+1);
for(int i=1;;i++)
if(k>tob[i])k-=tob[i];
else{
for(int j=L[i];;j++)
if(k>tot[j])k-=tot[j];
else{ans=j;break;}
break;
}
}
else{
get(s[bl],l-L[bl],R[bl]-l+1);get(s[br],0,r-L[br]+1);
for(int i=1;;i++)
if(k>tob[i]+prb[i][br-1]-prb[i][bl])
k-=tob[i]+prb[i][br-1]-prb[i][bl];
else{
for(int j=L[i];;j++)
if(k>tot[j]+pre[j][br-1]-pre[j][bl])k-=tot[j]+pre[j][br-1]-pre[j][bl];
else{ans=j;break;}
break;
}
}
printf("%d\n",--ans);
for(int i=0;i<=stak;i++)tot[sta[i]]=tob[bel(sta[i])]=0;
break;
}
case'M':{
int x=read()^ans;
int v=(read()^ans)+1;
const int id=bel(x);
int old=modify(s[id],x-L[id],v);
int *Po=pre[old],*Pn=pre[v],*Bo=prb[bel(old)],*Bn=prb[bel(v)];
for(int i=id;i<=blocks;i++)--Po[i],--Bo[i],++Pn[i],++Bn[i];
break;
}
case'I':{
int x=read()^ans;
int v=(read()^ans)+1;
const int id=bel(x);
iter it=s[id].begin();
for(int i=x-L[id];i--;)++it;
s[id].insert(it,v);
int *P=pre[v],*B=prb[bel(v)];
for(int i=id;i<=blocks;i++)++P[i],++B[i];
for(int i=id+1;i<=blocks;i++)
if(s[i-1].size()<=siz)break;
else{
int vl=s[i-1].back();
--pre[vl][i-1];--prb[bel(vl)][i-1];
s[i-1].pop_back();
s[i].push_front(vl);
}
break;
}
}
}
return 0;
}