P4119 未来日记

有一个长为 nn 的序列 aa,有 mm 次操作:

  • 把区间 [l,r][l,r] 内所有的 xx 变成 yy
  • 查询区间 [l,r][l,r] 内第 kk 小值。

1n,m,ai1051 \leq n,m,a_i \leq 10^5,时限 1.00s1.00\text{s},空限 512MB512\text{MB}

sol

最初分块。

难度评分:8.58.5

下面先假定 VV 为值域。

先看查询区间第 kk 大。

可以维护每个数在区间内出现了多少次。

这个可以用一个二维的 sumsum 数组维护:sum[i][j]sum[i][j] 表示前 ii 块中 jj 出现的次数。

这样区间内一个数 xx 出现的次数就是中间整块 sumsum的两个前缀和相减,再加上两边散块的出现次数,散块的出现个数可暴力。

然而这样时间复杂度为 O(nV)\mathcal O(nV),无法通过。

考虑优化。

考虑再加上值域分块。

首先预处理 cnt1[i][j]cnt1[i][j] 表示前 ii 块中在第 jj 块值域的数出现次数,cnt2[i][j]cnt2[i][j] 表示前 ii 块中 jj 出现次数。

预处理 O(nn)\mathcal{O}(n \sqrt{n})

这样查询的时候,整块用 cnt1cnt1cnt2cnt2 处理,散块用桶维护一下即可,单次时间复杂度为 O(n)\mathcal{O}(\sqrt{n})

再看修改。

散块暴力即可。

再考虑并查集,记 id[x][i]id[x][i] 为第 xx 块中第一个值为 ii 的数的下标。

同时其逆也记下,记 rid[x][i]rid[x][i] 表示第 xx 块中下标 ii 对应的值。

再用 posipos_i 记录每一位的 idid。(ii 是序列上位置)

如果要还原序列,只需让 ai=rid[bli][posi]a_i=rid[bl_i][pos_i]

再分情况讨论。

  • 块内无 xx,跳过。
  • 块内有 xxyy,可将 id[bl][y]=id[bl][x]id[bl][y]=id[bl][x],同时 rid[bl][id[bl][x]]=yrid[bl][id[bl][x]]=y,记得 id[bl][x]=0id[bl][x]=0
  • 块内有 x,yx,y, 考虑此时块内权值种类少 11, 而权值种类总共不超过 n+mn+m, 均摊后暴力重构复杂度总和为 O((n+m)n)\mathcal O((n+m)\sqrt{n}), 暴力重构即可,对于两个 cntcnt 数组的更新,可以先将其差分为每块的,最后再合并,一次 O(n)O(\sqrt{n})

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

总结:分块套分块,加套路并查集。

5.97s / 202.38MB / 6.29KB C++98 O2\text{5.97s / 202.38MB / 6.29KB C++98 O2}

#include <cstdio>
#include <cmath>
#include <algorithm>

namespace Fread
{
    const int SIZE = 1 << 21;
    char buf[SIZE], *S, *T;
    inline char getchar()
    {
        if (S == T)
        {
            T = (S = buf) + fread(buf, 1, SIZE, stdin);
            if (S == T)
                return '\n';
        }
        return *S++;
    }
}
namespace Fwrite
{
    const int SIZE = 1 << 21;
    char buf[SIZE], *S = buf, *T = buf + SIZE;
    inline void flush()
    {
        fwrite(buf, 1, S - buf, stdout);
        S = buf;
    }
    inline void putchar(char c)
    {
        *S++ = c;
        if (S == T)
            flush();
    }
    struct NTR
    {
        ~NTR()
        {
            flush();
        }
    } ztr;
}

#ifdef ONLINE_JUDGE
#define getchar Fread::getchar
#define putchar Fwrite::putchar
#endif

inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x * f;
}

inline void write(int x)
{
    if (x < 0)
    {
        putchar('-');
        x = -x;
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

const int N = 1e5 + 7, SQ = 620;

int n, m, a[N], len, bl[N], L[N], R[N], sum1[SQ], sum2[N];

int id[SQ][N], rid[SQ][N], pos[N];

int cnt1[SQ][SQ], cnt2[SQ][N];

inline void build(int x)
{
    int tot = 0;
    for (int i = 1; i <= len; ++i)
        id[x][rid[x][i]] = 0;
    for (int i = L[x]; i <= R[x]; ++i)
        if (!id[x][a[i]])
        {
            id[x][a[i]] = ++tot;
            rid[x][tot] = a[i];
        }
    for (int i = L[x]; i <= R[x]; ++i)
        pos[i] = id[x][a[i]];
}

inline void rest(int x)
{
    for (int i = L[x]; i <= R[x]; ++i)
        a[i] = rid[x][pos[i]];
}

inline void cg(int l, int r, int x, int y)
{
    for (int i = l; i <= r; ++i)
        if (a[i] == x)
        {
            --cnt1[bl[l]][bl[x]], ++cnt1[bl[l]][bl[y]];
            --cnt2[bl[l]][x], ++cnt2[bl[l]][y];
            a[i] = y;
        }
}

inline void change(int i, int x, int y)
{
    id[i][y] = id[i][x];
    rid[i][id[i][x]] = y;
    id[i][x] = 0;
}

inline void modify(int l, int r, int x, int y)
{
    if (x == y || cnt2[bl[r]][x] - cnt2[bl[l] - 1][x] == 0)
        return;
    for (int i = bl[n]; i >= bl[l]; --i)
    {
        cnt2[i][x] -= cnt2[i - 1][x];
        cnt2[i][y] -= cnt2[i - 1][y];
        cnt1[i][bl[x]] -= cnt1[i - 1][bl[x]];
        cnt1[i][bl[y]] -= cnt1[i - 1][bl[y]];
    }
    if (bl[l] == bl[r])
    {
        rest(bl[l]), cg(l, r, x, y);
        build(bl[l]);
        for (int i = bl[l]; i <= bl[n]; ++i)
        {
            cnt2[i][x] += cnt2[i - 1][x];
            cnt2[i][y] += cnt2[i - 1][y];
            cnt1[i][bl[x]] += cnt1[i - 1][bl[x]];
            cnt1[i][bl[y]] += cnt1[i - 1][bl[y]];
        }
        return;
    }
    rest(bl[l]), rest(bl[r]);
    cg(l, R[bl[l]], x, y), cg(L[bl[r]], r, x, y);
    build(bl[l]), build(bl[r]);
    for (int i = bl[l] + 1; i < bl[r]; ++i)
    {
        if (!cnt2[i][x])
            continue;
        if (cnt2[i][y])
        {
            rest(i), cg(L[i], R[i], x, y);
            build(i);
        }
        else
        {
            cnt1[i][bl[y]] += cnt2[i][x];
            cnt1[i][bl[x]] -= cnt2[i][x];
            cnt2[i][y] = cnt2[i][x];
            cnt2[i][x] = 0;
            change(i, x, y);
        }
    }
    for (int i = bl[l]; i <= bl[n]; ++i)
    {
        cnt2[i][x] += cnt2[i - 1][x];
        cnt2[i][y] += cnt2[i - 1][y];
        cnt1[i][bl[x]] += cnt1[i - 1][bl[x]];
        cnt1[i][bl[y]] += cnt1[i - 1][bl[y]];
    }
}

inline int kth(int l, int r, int k)
{
    int sum = 0;
    if (bl[l] == bl[r])
    {
        rest(bl[l]);
        for (int i = l; i <= r; ++i)
            sum2[i] = a[i];
        std::nth_element(sum2 + l, sum2 + l + k - 1, sum2 + r + 1);
        int ans = sum2[l + k - 1];
        for (int i = l; i <= r; ++i)
            sum2[i] = 0;
        return ans;
    }
    rest(bl[l]), rest(bl[r]);
    for (int i = l; i <= R[bl[l]]; ++i)
    {
        ++sum1[bl[a[i]]];
        ++sum2[a[i]];
    }
    for (int i = L[bl[r]]; i <= r; ++i)
    {
        ++sum1[bl[a[i]]];
        ++sum2[a[i]];
    }
    for (int i = 1; i <= (N - 1) / len + 1; ++i)
        if (sum + sum1[i] + cnt1[bl[r] - 1][i] - cnt1[bl[l]][i] >= k)
        {
            for (int j = (i - 1) * len + 1; j <= i * len; ++j)
                if (sum + sum2[j] + cnt2[bl[r] - 1][j] - cnt2[bl[l]][j] >= k)
                {
                    for (int i = l; i <= R[bl[l]]; ++i)
                    {
                        --sum1[bl[a[i]]];
                        --sum2[a[i]];
                    }
                    for (int i = L[bl[r]]; i <= r; ++i)
                    {
                        --sum1[bl[a[i]]];
                        --sum2[a[i]] = 0;
                    }
                    return j;
                }
                else
                    sum += sum2[j] + cnt2[bl[r] - 1][j] - cnt2[bl[l]][j];
        }
        else
            sum += sum1[i] + cnt1[bl[r] - 1][i] - cnt1[bl[l]][i];
}

int main()
{
    n = read(), m = read();
    len = sqrt(n * 8 / 5);
    for (int i = 1; i < N; ++i)
        bl[i] = (i - 1) / len + 1;
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    for (int i = 1; i <= bl[n]; ++i)
        L[i] = (i - 1) * len + 1, R[i] = i * len;
    R[bl[n]] = n;
    for (int i = 1; i <= bl[n]; ++i)
        build(i);
    for (int x = 1; x <= bl[n]; ++x)
    {
        for (int i = 1; i <= bl[N - 1]; ++i)
            cnt1[x][i] = cnt1[x - 1][i];
        for (int i = 1; i < N; ++i)
            cnt2[x][i] = cnt2[x - 1][i];
        for (int i = L[x]; i <= R[x]; ++i)
            ++cnt1[x][bl[a[i]]], ++cnt2[x][a[i]];
    }
    for (int i = 1, opt, l, r, x, y; i <= m; ++i)
    {
        opt = read(), l = read(), r = read();
        if (opt == 1)
        {
            x = read(), y = read();
            modify(l, r, x, y);
        }
        else
        {
            x = read();
            write(kth(l, r, x));
            putchar('\n');
        }
    }
    return 0;
}
posted @ 2022-01-25 09:27  蒟蒻orz  阅读(4)  评论(0编辑  收藏  举报  来源