LUOGU P4119 Ynoi2018 Future Diary

Posted by stickynote427 on Wed, 02 Feb 2022 23:26:00 +0100

Better reading experience

meaning of the title

There is a sequence with a length of \ (n \), with a total of \ (m \) operations:

  • 1 l r x y, change all \ (x \) in the interval \ ([l, r] \) into \ (Y \);
  • 2 l r k, query the smallest value of \ (k \) in the interval \ ([l, r] \)

\(1\le n, m\le 10^5 \), any time \ (1\le a_i\le 10^5 \)

Problem solution

For this complex modification operation, we directly consider blocking

Block the sequence and range at the same time, so that \ (sum {i, v} \) represents the number of values \ (v \) in the previous \ (i \) block, and \ (blsum {i, j} \) represents the number of values in the previous \ (i \) block in the range \ (j \)

Considering the query, for hash blocks, each query constructs \ (sctsum {v} \) to represent the number of values in the hash block with \ (v \), and \ (sctblsum {i} \) to represent the number of values in the value range \ (i \) block

When querying, we can \ (\ Theta(1) \) query the number of values in the interval \ ([l, r] \) that are \ (v \) and the number of values in the interval in the \ (i \) block of the value range

First enumerate which block the small value of \ (k \) is in the value range, and then enumerate which block the small value of \ (k \) is. The time complexity of a query is \ (\ Theta(\sqrt{n}) \)

Consider modification. For scattered blocks, you can modify them directly

For each whole block, each value in the block is given a label independently. Those with the same color have the same label. It is also necessary to maintain the mapping of value to label and value to label

There are two situations when changing \ (x \) to \ (y \):

  • No \ (y \) in block
    Map the label corresponding to \ (x \) to \ (y \), map \ (y \) to the label corresponding to the original \ (x \), and clear the mapping of \ (x \)
  • There is \ (y \) in the block
    It's not easy to deal with. It's just like violent modification of scattered blocks
    Consider the time complexity of doing so
    The number of violent modifications is no more than the sum of the number of different values in all blocks. There are \ (\ mathcal{O}(n) \) at the beginning. Each modification will not increase the whole block, and only scattered blocks may increase by 1. Therefore, there are \ (\ mathcal{O}(n+m) \) violent modifications in total, and the total complexity of violent modifications is \ (\ mathcal{O}((n+m)\sqrt{n}) \)

"rebuild" the block before each violent modification, that is, reassign the label and regenerate the two mappings
Every time you operate on a hash block, you must "reset" the block before it, that is, map each value with a label to get a new value

The total time complexity is \ (\ mathcal{O}((n+m)\sqrt{n}) \)

code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100000, maxvs = 316;
const int maxs = 360, maxc = 278;
int cnt;
int a[maxn + 5];
int lp[maxc + 5], rp[maxvs + 5];
int bl[maxn + 5];
int vlp[maxvs + 5];
int vbl[maxn + 5];
int sum[maxc + 5][maxn + 5];
int blsum[maxc + 5][maxvs + 5];
int rep[maxc + 5][maxn + 5];
int col[maxc + 5][maxs + 5];
int id[maxn + 5];
int ccnt[maxc + 5];
void buildUnion(int bid) {
    for (int i = 1; i <= ccnt[bid]; i++)
        rep[bid][col[bid][i]] = 0;
    ccnt[bid] = 0;
    for (int i = lp[bid]; i <= rp[bid]; i++) {
        if (rep[bid][a[i]] == 0) {
            rep[bid][a[i]] = ++ccnt[bid];
            col[bid][ccnt[bid]] = a[i];
        }
        id[i] = rep[bid][a[i]];
    }
}
void reset(int bid) {
    for (int i = lp[bid]; i <= rp[bid]; i++)
        a[i] = col[bid][id[i]];
}
void build(int n) {
    int siz = min(n, int(sqrt(1.3 * n)));
    cnt = ceil(1. * n / siz);
    for (int i = 1; i <= n; i++)
        bl[i] = (i - 1) / siz + 1;
    for (int i = 1; i <= cnt; i++) {
        lp[i] = (i - 1) * siz + 1;
        rp[i] = min(n, i * siz);
    }
    for (int i = 1; i <= maxn; i++)
        vbl[i] = (i - 1) / maxvs + 1;
    for (int i = 1; i <= maxvs + 1; i++)
        vlp[i] = (i - 1) * maxvs + 1;
    for (int i = 1; i <= cnt; i++) {
        for (int j = 1; j <= maxn; j++)
            sum[i][j] = sum[i - 1][j];
        for (int j = 1; j <= maxvs + 1; j++)
            blsum[i][j] = blsum[i - 1][j];
        for (int j = lp[i]; j <= rp[i]; j++) {
            sum[i][a[j]]++;
            blsum[i][vbl[a[j]]]++;
        }
        buildUnion(i);
    }
}
int tmp[maxn + 5], bltmp[maxvs + 5];
void add(int l, int r, int delta) {
    for (int i = l; i <= r; i++) {
        tmp[a[i]] += delta;
        bltmp[vbl[a[i]]] += delta;
    }
}
int query(int l, int r, int k) {
    int lb = bl[l], rb = bl[r];
    int res;
    if (lb == rb) {
        reset(lb);
        copy(a + l, a + r + 1, tmp + l);
        nth_element(tmp + l, tmp + l + k - 1, tmp + r + 1);
        res = tmp[l + k - 1];
        fill(tmp + l, tmp + r + 1, 0);
    } else {
        reset(lb);
        reset(rb);
        add(l, rp[lb], 1);
        add(lp[rb], r, 1);
        int i = 1, delta;
        for (; k - (delta = bltmp[i] + blsum[rb - 1][i] - blsum[lb][i]) > 0;
             i++)
            k -= delta;
        int j = vlp[i];
        for (; k - (delta = tmp[j] + sum[rb - 1][j] - sum[lb][j]) > 0; j++)
            k -= delta;
        res = j;
        add(l, rp[lb], -1);
        add(lp[rb], r, -1);
    }
    return res;
}
void change(int l, int r, int x, int y, int bid) {
    int chcnt = 0;
    for (int i = l; i <= r; i++) {
        if (a[i] == x) {
            a[i] = y;
            chcnt++;
        }
    }
    sum[bid][x] -= chcnt;
    sum[bid][y] += chcnt;
    blsum[bid][vbl[x]] -= chcnt;
    blsum[bid][vbl[y]] += chcnt;
}
void changeBlock(int l, int r, int x, int y, int bid) {
    reset(bid);
    change(l, r, x, y, bid);
    buildUnion(bid);
}
void modify(int l, int r, int x, int y) {
    int lb = bl[l], rb = bl[r];
    if (x == y || sum[rb][x] - sum[lb - 1][x] == 0)
        return;
    for (int i = cnt; i >= lb; i--) {
        sum[i][x] -= sum[i - 1][x];
        sum[i][y] -= sum[i - 1][y];
        blsum[i][vbl[x]] -= blsum[i - 1][vbl[x]];
        blsum[i][vbl[y]] -= blsum[i - 1][vbl[y]];
    }
    if (lb == rb)
        changeBlock(l, r, x, y, lb);
    else {
        changeBlock(l, rp[lb], x, y, lb);
        changeBlock(lp[rb], r, x, y, rb);
        for (int i = lb + 1; i < rb; i++) {
            if (sum[i][x] == 0)
                continue;
            if (sum[i][y] == 0) {
                col[i][rep[i][x]] = y;
                swap(rep[i][y], rep[i][x]);
                blsum[i][vbl[y]] += sum[i][x];
                blsum[i][vbl[x]] -= sum[i][x];
                sum[i][y] = sum[i][x];
                sum[i][x] = 0;
            } else
                changeBlock(lp[i], rp[i], x, y, i);
        }
    }
    for (int i = lb; i <= cnt; i++) {
        sum[i][x] += sum[i - 1][x];
        sum[i][y] += sum[i - 1][y];
        blsum[i][vbl[x]] += blsum[i - 1][vbl[x]];
        blsum[i][vbl[y]] += blsum[i - 1][vbl[y]];
    }
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    build(n);
    for (int i = 1; i <= m; i++) {
        int op, l, r;
        scanf("%d%d%d", &op, &l, &r);
        if (op == 1) {
            int x, y;
            scanf("%d%d", &x, &y);
            modify(l, r, x, y);
        } else {
            int k;
            scanf("%d", &k);
            printf("%d\n", query(l, r, k));
        }
    }
}

Topics: partitioning