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}$,值域的前缀和同理。
修改很简单,就是遍历后面的所有块,依次更新前缀和即可。
对于插入,我们直接把数插入其应该在的位置,其所在块在它后面的元素直接暴力后移,同理更新前缀和。
代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }