带插入区间K小值 - 更为优秀的解法
新写一篇题解,彻底了结这道毒瘤卡常题。
从我的上一篇带插入区间K小值题解最后得到了一个时间复杂度为 \(O(n \times \sqrt {n \log {n}} \times \log {n})\)的解法,凭借优越的常数得到了60分的成绩,现在又有了一种更为优秀的做法。
用块状链表可以实现单次插入 \(O(\sqrt {n})\)的复杂度,假设没有了插入操作,就变成了这样一个题目:
- 修改某一位置的值;
- 询问区间第\(k\)小。
这其实是lxl的Ynoi 2018 未来日记的弱化版。由于没有插入操作,所以可以将数列分成\(\sqrt {n}\) 块,同时将权值分块,令\(val[i][j]\) 表示前\(i\)块权值在第\(j\)个值域的数的个数,\(w[i][j]\)表示前\(i\)块权值为\(j\)的数的个数。修改暴力跑一遍前缀和即可,询问先早答案暴力从小到大枚举在哪一个值域里,然后在值域里暴力枚举答案即可。将分块写成块状链表,即可 \(O(n \sqrt {n})\)的复杂度,适当卡常即可通过此题。
代码:
#include<bits/stdc++.h>
#pragma GCC optimize(3)//找一个人少的时候用这个卡过去
#define re register
using namespace std;
const int N=70000,M=275;
struct BLO {
int lf,rf;
int sz,a[M*2+50],val[M+10],w[N+10];
//a[]:原数列 val[]:权值分块 w[]:权值
};BLO t[M+10]; int hed=0,tot=0;
int n,m,ans=0,Val[M+10],W[N+10];
char pr[1000010]; int top=0;
inline int read() {
re int x=0;re char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
inline int Min(int x,int y) { return x<y? x:y; }
void print(int x) { if(x>9) print(x/10); pr[top++]=x%10+'0'; }//输出优化
void init() {
n=read(),hed=0,tot=n/M+1;
for(re int i=1,x;i<=n;++i) {
x=read(),t[i/M+1].a[++t[i/M+1].sz]=x;
++t[i/M+1].w[x],++t[i/M+1].val[x/M+1];
}
for(int i=0;i<tot;++i) t[i].rf=i+1;
for(int i=1;i<=tot;++i) t[i].lf=i-1;
for(int i=1;i<=tot;++i) {
for(int j=0;j<=N;j++) t[i].w[j]+=t[i-1].w[j];
for(int j=1;j<=M;j++) t[i].val[j]+=t[i-1].val[j];
}
m=read();
}
void split(int u) {
++tot;
t[tot].lf=t[u].lf,t[t[u].lf].rf=tot,t[tot].rf=u,t[u].lf=tot;
for(re int i=0;i<=N;i++) t[tot].w[i]=t[u].w[i];
for(re int i=1;i<=M;i++) t[tot].val[i]=t[u].val[i];
for(re int i=1;i<=M;i++) t[tot].a[i]=t[u].a[i];
for(re int i=M+1;i<=t[u].sz;++i)
t[u].a[i-M]=t[u].a[i],
--t[tot].val[t[u].a[i]/M+1],--t[tot].w[t[u].a[i]];
t[tot].sz=M,t[u].sz-=M;
}
void insert(int x,int k) {
int u=t[hed].rf; for(;x-1>t[u].sz;x-=t[u].sz,u=t[u].rf);
++t[u].sz;
for(re int i=t[u].sz;i>x;--i) t[u].a[i]=t[u].a[i-1];
t[u].a[x]=k; int tmp=u;
while(u) ++t[u].val[k/M+1],++t[u].w[k],u=t[u].rf;
if(t[tmp].sz==M*2) split(tmp);
}
void modify(int x,int New) {
int u=t[hed].rf; for(;x>t[u].sz;x-=t[u].sz,u=t[u].rf);
int Old=t[u].a[x]; t[u].a[x]=New;
while(u)
--t[u].val[Old/M+1],--t[u].w[Old],
++t[u].val[New/M+1],++t[u].w[New],
u=t[u].rf;
}
void query(int x,int y,int k) {
int u=t[hed].rf,v=t[hed].rf; ans=0; bool flag=0;
for(;x>t[u].sz;x-=t[u].sz,u=t[u].rf);
for(;y>t[v].sz;y-=t[v].sz,v=t[v].rf);
for(re int i=1;i<x;++i)//边角块
--Val[t[u].a[i]/M+1],--W[t[u].a[i]];
for(re int i=1;i<=y;++i)
++Val[t[v].a[i]/M+1],++W[t[v].a[i]];
for(int i=1;;++i) {
if(Val[i]+t[t[v].lf].val[i]-t[t[u].lf].val[i]>=k) {
int mx=Min(i*M,N+1);
for(int j=(i-1)*M;j<mx;++j) {
if(W[j]+t[t[v].lf].w[j]-t[t[u].lf].w[j]>=k)
{ ans=j,flag=1;break; }
else
k-=(W[j]+t[t[v].lf].w[j]-t[t[u].lf].w[j]);
}
break;
}
k-=(Val[i]+t[t[v].lf].val[i]-t[t[u].lf].val[i]);
}
for(re int i=1;i<x;++i)//恢复(比memset快)
++Val[t[u].a[i]/M+1],++W[t[u].a[i]];
for(re int i=1;i<=y;++i)
--Val[t[v].a[i]/M+1],--W[t[v].a[i]];
}
void debug() {
for(int i=t[hed].rf;i;i=t[i].rf) {
printf(" ");
for(int j=1;j<=t[i].sz;j++) printf("%d ",t[i].a[j]);
}
printf("\n");
}
int main() {
#ifndef ONLINE_JUDGE
freopen("Dynamic Kth in range.in","r",stdin);
freopen("Dynamic Kth in range.out","w",stdout);
int cur=clock();
#endif
init(); int x,y,k; char opt;
for(;m;--m) {
opt=getchar();
while(opt<'A'||opt>'Z') opt=getchar();
x=read()^ans,y=read()^ans;
switch(opt) {
case 'Q':
k=read()^ans,query(x,y,k);
print(ans),pr[top++]=10;
break;
case 'I': insert(x,y);break;
default: modify(x,y);break;
}
#ifndef ONLINE_JUDGE
ans=0;
#endif
}
pr[--top]='\0',puts(pr);
#ifndef ONLINE_JUDGE
printf(">>> %d ms.\n",clock()-cur);
fclose(stdin),fclose(stdout);
#endif
return 0;
}