1. 程式人生 > >帶插入區間K小值 - 更為優秀的解法

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