帶插入區間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;
}