帶插入區間K小值 - 更為優秀的解法
阿新 • • 發佈:2018-12-27
新寫一篇題解,徹底了結這道毒瘤卡常題。
從我的上一篇帶插入區間K小值題解最後得到了一個時間複雜度為 \(O(n \times \sqrt {n \log {n}} \times \log {n})\)的解法,憑藉優越的常數得到了60分的成績,現在又有了一種更為優秀的做法。
用塊狀連結串列可以實現單次插入 \(O(\sqrt {n})\)的複雜度,假設沒有了插入操作,就變成了這樣一個題目:
- 修改某一位置的值;
- 詢問區間第\(k\)小。
這其實是lxl的Ynoi 2018 未來日記的弱化版。由於沒有插入操作,所以可以將數列分成\(\sqrt {n}\) 塊,同時將權值分塊,令\(val[i][j]\)
程式碼:
#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; }