1. 程式人生 > 實用技巧 >bzoj3065 區間帶插入最小值

bzoj3065 區間帶插入最小值

題意

一個長為$n$的數列,有$m$個操作,支援插入,修改,以及區間第$k$大的查詢,強制線上。

$n\le 35000$,$m\le 70000,$插入個數$\le 35000,$序列中數的權值$\le 70000$。

Sol

此題可以用分塊以及樹套樹做,這裡介紹分塊做法。

塊狀連結串列

嚴格來說,此題的做法為塊狀連結串列,綜合了連結串列插入方便和陣列查詢方便的優勢,而複雜度與分出塊的大小有關,最優為$\mathcal{O}(n\sqrt n)$,同分塊。

我們先簡單講解一下塊狀連結串列的實現:將數列分成一個個塊,然後用連結串列的形式將這些塊串起來。

但有的資料會讓所有的插入恰好在同一個塊裡,會導致複雜度爆炸,所以我們設定一個臨界值,當塊的大小大於臨界時就把塊分裂成兩個大小相同的塊,以保證複雜度。

struct block {
    int l, r, sz;        //連結串列中其左右的塊的編號
    int s[605], hv[75005], a[605];
    int& operator [] (int k) {return a[k];}
}b[605];
void split(int pos) {        //分裂塊
    ++cnt;
    if(b[pos].r != 0)  b[b[pos].r].l = cnt;      //繼承原塊的左右塊編號
    b[cnt].r = b[pos].r, b[pos].r = cnt;
    b[cnt].l 
= pos; memcpy(b[cnt].s, b[pos].s, sizeof(b[pos].s)); //複製資訊 memcpy(b[cnt].hv, b[pos].hv, sizeof(b[pos].hv)); int sz = b[pos].sz / 2; b[cnt].sz = b[pos].sz - sz; for(int i = sz + 1; i <= b[pos].sz; i++) b[cnt][i - sz] = b[pos][i]; b[pos].sz = sz; for(int i = 1; i <= b[cnt].sz; i++) --b[pos].s[belong[b[cnt][i]]], --b[pos].hv[b[cnt][i]]; //
消除後面塊的貢獻(本題要用) }

思路

考慮如何在$\mathcal{O}(n\sqrt n)$時間找第$k$大,我們可以利用值域分塊的思路,先求出$k$在哪個值域,再在值域中遍歷每個值,即可求出$k$的具體值。

實現方式:

  • 令最大值為$mx$, 記錄塊內值在$[1,\sqrt{mx}],[\sqrt{mx}+1, 2*\sqrt{mx}]...$內數的個數以及每個數具體出現的次數。
  • 從前到後遍歷上面記錄的陣列,記錄一個字首和,字首和第一次大於$0$時就代表了$k$在當前遍歷到的值域。
  • 再在值域內遍歷,用和上一步一樣的方法找到$k$的準確值。

由於原題是對區間$[l,r]$詢問,故記錄字首和即可,$s_{k, i}$表示塊$[1,k]$中值為$i$的個數,$g_{k,i}$表示塊$[1,k]$中值在第$i$個值域中數的個數,那麼值為$i$的數的個數即為$s_r-s_{l-1}$,值域的字首和同理。

修改很簡單,就是遍歷後面的所有塊,依次更新字首和即可。

對於插入,我們直接把數插入其應該在的位置,其所在塊在它後面的元素直接暴力後移,同理更新字首和。

程式碼

#include<bits/stdc++.h>
using namespace std;
int Read() {
    int x = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-')  f = -1; ch = getchar();}
    while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
    return x * f;
}
int n, m, base, a[75005], tmp[75005], tmpk[75005], stk[75005], cnt, lst;
int belong[75005], ll[305], rr[305], Cnt;
struct block {
    int l, r, sz;
    int s[605], hv[75005], a[605];
    int& operator [] (int k) {return a[k];}
}b[605];
void prework() {
    //base = sqrt(n * 2) * 2;
    base = 600;
    for(int i = 0; i <= 70000; i += 300) 
        ll[++Cnt] = i, rr[Cnt] = i + 299;
    rr[Cnt] = 70000;
    for(int i = 1; i <= Cnt; i++)
        for(int j = ll[i]; j <= rr[i]; j++)
            belong[j] = i;
    for(int i = 1; i <= n; i += base) {
        ++cnt;
        int L = i, R = min(i + base - 1, n);
        b[cnt].l = cnt - 1; b[cnt].sz = R - L + 1;
        if(L != 1)  b[cnt - 1].r = cnt; 
    }
    b[cnt].r = 0;
    int L, R = 0, val;
    for(int i = 1; i <= cnt; i++) {
        L = R + 1, R = L + b[i].sz - 1;
        if(i != 1)  memcpy(b[i].s, b[i - 1].s, sizeof(b[i - 1].s));
        if(i != 1)  memcpy(b[i].hv, b[i - 1].hv, sizeof(b[i - 1].hv));
        if(i == 1)  memset(b[i].s, 0, sizeof(b[i].s));
        if(i == 1)  memset(b[i].hv, 0, sizeof(b[i].hv));
        for(int j = L; j <= R; j++) {
            val = belong[a[j]];
            b[i][j - L + 1] = a[j];
            ++b[i].s[val];
            ++b[i].hv[a[j]];
        }
    }
}
void split(int pos) {
    ++cnt;
    if(b[pos].r != 0)  b[b[pos].r].l = cnt;
    b[cnt].r = b[pos].r, b[pos].r = cnt;
    b[cnt].l = pos;
    memcpy(b[cnt].s, b[pos].s, sizeof(b[pos].s));
    memcpy(b[cnt].hv, b[pos].hv, sizeof(b[pos].hv));
    int sz = b[pos].sz / 2;
    b[cnt].sz = b[pos].sz - sz;
    for(int i = sz + 1; i <= b[pos].sz; i++)  b[cnt][i - sz] = b[pos][i];
    b[pos].sz = sz;
    for(int i = 1; i <= b[cnt].sz; i++)  --b[pos].s[belong[b[cnt][i]]], --b[pos].hv[b[cnt][i]];
}
signed main() {
    n = Read();
    for(int i = 1; i <= n; i++)  a[i] = Read();
    prework();
    m = Read();
    for(int i = 1; i <= m; i++) {
        char ch = getchar();
        while(!isalpha(ch))  ch = getchar();
        if(ch == 'Q') {
            int l = Read() ^ lst, r = Read() ^ lst, k = Read() ^ lst;
            int sz = 0, L = 1, R = 1;
            while(sz + b[L].sz < l)  sz += b[L].sz, L = b[L].r;
            l = l - sz;
            sz = 0;
            while(sz + b[R].sz < r)  sz += b[R].sz, R = b[R].r;
            r = r - sz;
            if(L == R) {
                int tp = 0, pos;
                for(int i = l; i <= r; i++) {
                    stk[++tp] = b[L][i];
                    tmp[b[L][i]]++, tmpk[belong[b[L][i]]]++;
                }
                for(int i = 1; i <= Cnt; i++) {
                    if(k - tmpk[i] <= 0) {pos = i; break;}
                    k -= tmpk[i];
                }
                for(int i = ll[pos]; i <= rr[pos]; i++) {
                    if(k - tmp[i] <= 0) {pos = i; break;}
                    k -= tmp[i];
                }
                printf("%d\n", lst = pos);
                while(tp)  tmp[stk[tp]]--, tmpk[belong[stk[tp--]]]--;
                continue;
            }
            int tp = 0, pos;
            for(int i = l; i <= b[L].sz; i++) {
                stk[++tp] = b[L][i];
                tmp[b[L][i]]++, tmpk[belong[b[L][i]]]++;
            }
            for(int i = 1; i <= r; i++) {
                stk[++tp] = b[R][i];
                tmp[b[R][i]]++, tmpk[belong[b[R][i]]]++;
            }
            for(int i = 1; i <= Cnt; i++) {
                if(k - (b[b[R].l].s[i] - b[L].s[i] + tmpk[i]) <= 0) {pos = i; break;}
                k -= b[b[R].l].s[i] - b[L].s[i] + tmpk[i];
            }
            for(int i = ll[pos]; i <= rr[pos]; i++) {
                if(k - (b[b[R].l].hv[i] - b[L].hv[i] + tmp[i]) <= 0) {pos = i; break;}
                k -= b[b[R].l].hv[i] - b[L].hv[i] + tmp[i];
            }
            while(tp)  tmp[stk[tp]]--, tmpk[belong[stk[tp--]]]--;
            printf("%d\n", lst = pos);
        }
        if(ch == 'M') {
            int x = Read() ^ lst, y = Read() ^ lst, z = 1, sz = 0;
            while(sz + b[z].sz < x)  sz += b[z].sz, z = b[z].r;
            x = x - sz; int val = b[z][x];
            b[z][x] = y; x = val;
            while(z) {
                b[z].s[belong[x]]--, b[z].s[belong[y]]++;
                b[z].hv[x]--, b[z].hv[y]++;
                z = b[z].r;
            }
        }
        if(ch == 'I') {
            int x = Read() ^ lst, val = Read() ^ lst;
            int L = 1, sz = 0;
            if(x == n + 1) {
                while(b[L].r)  L = b[L].r;
                x = b[L].sz + 1;
            } 
            else {
                while(sz + b[L].sz < x)
                    sz += b[L].sz, L = b[L].r;
                x = x - sz;
            }
            int pos = L;
            while(pos) {
                b[pos].s[belong[val]]++;
                b[pos].hv[val]++;
                pos = b[pos].r;
            }
            int ccnt = b[L].sz + 1;
            if(x == b[L].sz + 1)  b[L][ccnt--] = val;
            for(int i = b[L].sz; i >= 1; i--) {
                b[L][ccnt--] = b[L][i];
                if(i == x)  b[L][ccnt--] = val;
            }
            ++b[L].sz;
            if(b[L].sz >= base)  split(L);
            ++n;
        }
    }
    return 0;
}
View Code