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

 

posted @ 2020-09-14 13:14  verjun  阅读(149)  评论(0编辑  收藏  举报