ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LUOGU P4119 Ynoi2018 未来日记

2022-02-02 18:32:50  阅读:187  来源: 互联网

标签:Ynoi2018 标号 lb int LUOGU sum P4119 散块 blsum


更好的阅读体验

题意

有一个长度为 \(n\) 的序列,共 \(m\) 次操作:

  • 1 l r x y,把区间 \([l, r]\) 内所有 \(x\) 变成 \(y\);
  • 2 l r k,查询区间 \([l, r]\) 内第 \(k\) 小值.

\(1\le n, m\le 10^5\),任何时刻 \(1\le a_i\le 10^5\)

题解

对于这种复杂的修改操作,我们直接考虑分块

对序列和值域同时分块,令 \(sum_{i, v}\) 表示前 \(i\) 块内值 \(v\) 的个数,\(blsum_{i, j}\) 表示序列前 \(i\) 块内值在值域第 \(j\) 块内的个数

考虑查询,对于散块我们每次查询构造 \(sctsum_{v}\) 表示在散块中值为 \(v\) 的个数,\(sctblsum_{i}\) 表示散块中值在值域第 \(i\) 块内的个数

查询时我们对整块差分再加上散块的贡献,即可 \(\Theta(1)\) 地查询区间 \([l, r]\) 内值为 \(v\) 的个数和区间内值在值域第 \(i\) 块中的个数

先枚举 \(k\) 小值在值域中的哪一块,再枚举 \(k\) 小值是该块中的哪一个值,一次查询的时间复杂度为 \(\Theta(\sqrt{n})\)

考虑修改,对于散块直接暴力修改即可

对于每个整块独立地给块内每一个值一个标号,颜色相同的有相同的标号,还需要维护值对标号的映射和值对标号的映射

把 \(x\) 变成 \(y\) 时的两种情况:

  • 块内没有 \(y\)
    将 \(x\) 对应的标号映射到 \(y\),将 \(y\) 映射到原本 \(x\) 对应的标号,将 \(x\) 的映射清空
  • 块内有 \(y\)
    不太好处理,直接像对散块一样暴力修改
    考虑这样做的时间复杂度
    暴力修改的次数不多于所有块内不同值个数的和,一开始有 \(\mathcal{O}(n)\) 个,每修改一次整块不会增加,只有散块可能增加1,所以总共暴力修改 \(\mathcal{O}(n+m)\) 次,总的暴力修改复杂度即为 \(\mathcal{O}((n+m)\sqrt{n})\)

每次暴力修改前都要 "rebuild" 该块,即重新分配标号,重新生成两个映射
每次操作散块都要前 "reset" 该块,即对于每个值用标号映射得到新的值

总的时间复杂度为 \(\mathcal{O}((n+m)\sqrt{n})\)

代码

#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));
        }
    }
}

标签:Ynoi2018,标号,lb,int,LUOGU,sum,P4119,散块,blsum
来源: https://www.cnblogs.com/gzezfisher/p/luogu4119.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有