bzoj3065 區間帶插入最小值
阿新 • • 發佈:2020-09-14
題意
一個長為$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