题解 P4278 【带插入区间K小值】
我是来给daniel14311531大佬的
1.分块怎么高效求第K大。 首先网上那些二分套主席树套分块之类的都不在我们的讨论范围,一点也不优美。
下面介绍一个大杀器,时代的新(对博主来说是新)科技:
值域分块。
就是对查询的值域进行分块。
具体来说就是设两个数组S1[]和S2[]
S1[i] = 值在
S2[i] = 值为
那么求第K大就可以从小到大枚举块,求出第K大在值域中的哪一块。
然后再在那一块中枚举权值。
两步都是
那么如果跨越多块,我们就需要做到
可以前缀和。
恰好 。。。。。这个S1,S2的修改是
2.如何在有插入的情况下维护分块。
块状链表。
就是不把块放在序列上。
前一块和后一块互相连接像链表一样。
块中是一个数组。
有
那么我们相当于是把对于插入-查询操作
真的比
#include<bits/stdc++.h>
#define maxn 70005
#define S 310
using namespace std;
char cb[1<<17],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<17,stdin),cs==ct)?0:*cs++)
inline void read(int &res){
char ch;
for(;!isdigit(ch=getc()););// if(ch=='-') f=1;
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
//(f) && (res=-res);
}
int n;
struct block{
int lb,rb;
int a[S*2+10],cntS[maxn/S+5],cnt[maxn],siz;
}B[maxn/S],Nb;
int flb = 1 , cnt_bl = 0 , Mx = 0;
int id[maxn];
int Query(int x,int y,int k){
int lb=1,rb=1,ret=0;
for(;B[lb].siz && B[lb].siz<x;x-=B[lb].siz,lb=B[lb].rb);
for(;B[rb].siz && B[rb].siz<y;y-=B[rb].siz,rb=B[rb].rb);
//printf("%d %d %d\n",lb,rb,B[1].siz);
if(lb == rb){
//printf("%d %d\n",x,y);
for(int i=x-1,j=y-1,tmp;i<=j;i++){
//printf("%d\n",id[B[lb].a[i]]);
tmp = B[lb].a[i];
Nb.cntS[id[tmp]]++,
Nb.cnt[tmp]++;
}
for(int i=1;i<=id[maxn-1];i++){
//printf("%d %d\n",Nb.cntS[i],k);
if(Nb.cntS[i]>=k){
for(int j=(i-1)*S;;j++){
//printf("%d %d\n",j,k);
if(k<=Nb.cnt[j]){ ret=j;break; }
else k-=Nb.cnt[j];
}
break;
}
else k-=Nb.cntS[i];
}
for(int i=x-1,j=y-1,tmp;i<=j;i++){
//printf("%d\n",id[B[lb].a[i]]);
tmp = B[lb].a[i];
Nb.cntS[id[tmp]]--,
Nb.cnt[tmp]--;
}
return ret;
}
for(int i=x-1,tmp;i<B[lb].siz;i++){
tmp = B[lb].a[i],
Nb.cntS[id[tmp]]++,
Nb.cnt[tmp]++;
}
for(int i=0,tmp;i<y;i++){
tmp = B[rb].a[i],
Nb.cntS[id[tmp]]++,
Nb.cnt[tmp]++;
}
rb=B[rb].lb;
for(int i=1,tmp;i<=id[Mx];i++){
tmp = Nb.cntS[i]+B[rb].cntS[i]-B[lb].cntS[i];
if(tmp>=k){
for(int j=(i-1)*S,res;;j++){
res = Nb.cnt[j]+B[rb].cnt[j]-B[lb].cnt[j];
if(k<=res){ ret=j;break; }
else k-=res;
}
break;
}
else k-=tmp;
}
rb=B[rb].rb;
for(int i=x-1,tmp;i<B[lb].siz;i++){
tmp = B[lb].a[i],
Nb.cntS[id[tmp]]--,
Nb.cnt[tmp]--;
}
for(int i=0,tmp;i<y;i++){
tmp = B[rb].a[i],
Nb.cntS[id[tmp]]--,
Nb.cnt[tmp]--;
}
return ret;
}
void Modify(int x,int y){
int lb=1;
for(;B[lb].siz && B[lb].siz<x;x-=B[lb].siz,lb=B[lb].rb);
int py = B[lb].a[x-1]; B[lb].a[x-1] = y;
for(;lb;lb=B[lb].rb)
B[lb].cntS[id[py]]--,
B[lb].cntS[id[y]]++,
B[lb].cnt[py]--,
B[lb].cnt[y]++;
}
void split(int x){
B[++cnt_bl].lb=x,B[cnt_bl].rb=B[x].rb;
B[B[x].rb].lb=cnt_bl,B[x].rb=cnt_bl;
B[cnt_bl].siz = S , B[x].siz -= S;
memcpy(B[cnt_bl].a,B[x].a+B[x].siz,S*sizeof(int));
memcpy(B[cnt_bl].cntS,B[x].cntS,sizeof B[x].cntS);
memcpy(B[cnt_bl].cnt,B[x].cnt,sizeof B[x].cnt);
for(int i=0;i<S;i++)
B[x].cntS[id[B[cnt_bl].a[i]]]--,
B[x].cnt[B[cnt_bl].a[i]]--;
}
void Insert(int x,int y){
int lb=1;x--;
for(;B[lb].siz && B[lb].siz<x;x-=B[lb].siz,lb=B[lb].rb);
for(int i=B[lb].siz++;i>x;i--) B[lb].a[i] = B[lb].a[i-1];
B[lb].a[x]=y;
for(int i=lb;i;i=B[i].rb)
B[i].cntS[id[y]]++,
B[i].cnt[y]++;
if(B[lb].siz>=2*S)
split(lb);
}
int main(){
//int t1 = clock();
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
read(n);
for(int i=0;i<maxn;i++) id[i]=i/S+1;
cnt_bl=id[n-1];
for(int i=0;i<n;i++){
int u = id[i] , x;
read(x);
B[u].a[B[u].siz++] = x;
B[u].cntS[id[x]]++;
B[u].cnt[x]++;
Mx = max(Mx , x);
}
for(int i=2;i<=cnt_bl;i++)
{
B[i-1].rb=i,B[i].lb=i-1;
for(int j=0;j<=Mx;j++){
B[i].cnt[j] += B[i-1].cnt[j];
if(j==0 || id[j]!=id[j-1])
B[i].cntS[id[j]] += B[i-1].cntS[id[j]];
}
}
int q;
read(q);
char s[2];
for(int x,y,k,la=0;q--;){
while(!isalpha(s[0]=getc()));
//puts(s);
//printf("@@@@%d\n",allsiz);
if(s[0] == 'Q'){
read(x),read(y),read(k);
x ^= la , y ^= la , k ^= la;
//printf("@@%d %d %d\n",x,y,k);
printf("%d\n",la=Query(x,y,k));
}
else if(s[0] == 'M'){
read(x),read(y);
x ^= la , y ^= la;
Mx = max(Mx ,y);
//printf("$$%d %d\n",x,y);
Modify(x,y);
}
else {
read(x),read(y);
x^=la , y ^= la;
Mx = max(Mx , y);
//printf("##%d %d\n",x,y);
Insert(x,y);
}
//puts(s);
//printf("%d\n",B[1].siz);
}
//freopen("CON","w",stdout);
//printf("%d\n",clock()-t1);
}