题解 CF896E Welcome home, Chtholly

$\texttt{link}$

update on 2021.11.12 修正了一些错误。

题意

给定一个长为 $n$ 的序列 $a$,$m$ 次操作:

  • 将 $a_{l,\cdots,r}$ 中大于 $x$ 的数减去 $x$。
  • 查询区间 $a_{l,\cdots,r}$ 中等于 $x$ 的数的个数。

数据范围:$1\le n,m\le10^5,0\le a_i,x\le10^5$

题解

设 $w$ 为值域。

对于每个块内,记录每一个值出现的次数,维护一个最大值 $maxn$ 和一个区间减的标记。

对于修改操作,散块直接暴力重构。

若修改一个整块,分类讨论一下,设更新所有值为 $v$ 的数时间复杂度为 $O(\text{ds})$:

  • 若 $maxn\le 2x$,把块内所有大于 $x$ 的数减去 $x$。此时需更新的区间为 $\left[x+1,maxn\right]$,此时用 $O(maxn-x)\times O(\text{ds})$ 的代价使 $maxn$ 减少了 $maxn-x$。

  • 若 $maxn>2x$,把块内所有小于等于 $x$ 的数加上 $x$,再打上区间减 $x$ 的标记,需更新的区间为 $\left[1,x\right]$,此时用 $O(x)\times O(\text{ds})$ 的代价使 $maxn$ 的代价减少了 $x$。

首先所有的值会越来越小,又 $maxn$ 比 $2x$ 大就会打标记,这就保证了 $maxn$ 不会溢出,每个值每个块最多会修改 $w$ 次,复杂度即为 $O(w)$。

散块查询暴力统计,整块查询返回块内值 $x$ 记录的个数即可,时间复杂度为 $O(\sqrt{n})$。

空间复杂度为即为 $O(w\sqrt{n})$,$w$ 为值域。

考虑怎么更新每个值对应的一堆数。

比较好写的写法是并查集,对于任意一个其值为 $v$ 的并查集,维护一个 $rt$,表示序列中第一个值为 $v$ 的数的下标,对于等于同一个值 $x$ 的数,则全并到 $fa_{rt_x}$ 上。

在合并的时候,设把值为 $x$ 的并查集合并到值为 $y$ 的并查集上。

  • 若 $rt_y$ 不存在,则把 $rt_x$ 全放到 $rt_y$ 上,并使 $rt_y$ 指向 $y$。
  • 若 $rt_x$ 存在,则直接合并 $rt_x$ 和 $rt_y$ 两个并查集。

用这种并查集可把维护每个块的复杂度优化到了 $O(\alpha(n))\approx O(1)$,总体时间复杂度是 $O((n+m)\sqrt{n})$,空间复杂度为 $O(w\sqrt{n})$。

已经足够通过本题,然而还可以优化。

有个逐块处理的 trick,即离线下来一块一块处理,可把空间复杂度优化到 $O(w)$。

也许可以加启发式合并?但我不会写,而且重构次数较多,启发式合并优化意义不大。

#include <bits/stdc++.h>
using namespace std;
namespace IO {
    #if ONLINE_JUDGE
    #define getc() (IS == IT && (IT = (IS = ibuf) + fread(ibuf, 1, IL, stdin), IS == IT) ? EOF : *IS++)
    #else
    #define getc() getchar()
    #endif
    const int IL = 1 << 20, OL = 1 << 21;
    int olen = 0; 
    char ibuf[IL], *IS = ibuf, *IT = ibuf, obuf[OL];
    inline int read() {
        register char ch = getc(); register int f = 1, x = 0;
        while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getc(); }
        while(isdigit(ch)) x = x * 10 + ch - 48, ch = getc();
        return x * f;
    }
    inline void flush() { fwrite(obuf, 1, olen, stdout); olen = 0; }
    inline void putc(register char ch) { obuf[olen++] = ch; }
    template<class T>
    inline void write(register T x) {
        if(x < 0) obuf[olen++] = '-', x = -x;
        if(x > 9) write(x / 10);
        obuf[olen++] = x % 10 + 48;
    }
} using namespace IO;
const int N = 1e6 + 10, M = 5e5 + 10, W = 1e5 + 10;
int n, m;
int a[N], val[N], s, t, fa[N], siz[N], rt[W], mx, tag, L;
struct ask {
    int opt, l, r, x, ans;
}q[M];
inline int find(int x) {
    if(fa[x] == x)
        return x;
    return fa[x] = find(fa[x]);
}
inline void init() { L = sqrt(n); }
inline void build() {
    mx = tag = 0;
    for(register int i = s; i <= t; i++) {
        mx = max(mx, a[i]);
        if(!rt[a[i]])
            fa[i] = rt[a[i]] = i, val[i] = a[i];
        else
            fa[i] = rt[a[i]];
        siz[a[i]]++;
    }
}
inline void merge(int x, int y)  {
    if(rt[y]) fa[rt[x]] = rt[y];
    else {
        rt[y] = rt[x];
        val[rt[y]] = y;
    }
    siz[y] += siz[x];
    rt[x] = siz[x] = 0;
}
inline void total_update(int x) {
    if(mx - tag > (x << 1)) {
        for(register int i = tag + 1; i <= tag + x; i++)
            if(rt[i])
                merge(i, i + x);
        tag += x;
    }
    else {
        for(register int i = mx; i > tag + x; i--)
            if(rt[i])
                merge(i, i - x);
        mx = min(mx, tag + x);
    }
}
inline void part_update(int l, int r, int x) {
    for(register int i = s; i <= t; i++) {
        a[i] = val[find(i)];
        rt[a[i]] = siz[a[i]] = 0;
        a[i] -= tag;
    }
    int nl = max(s, l), nr = min(t, r);
    for(register int i = s; i <= t; i++)
        val[i] = fa[i] = 0;
    for(int i = nl; i <= nr; i++)
        a[i] -= x * (a[i] > x);
    build();
} 
inline void query(int num) {
    if(q[num].l <= s && t <= q[num].r) 
        q[num].ans += siz[q[num].x + tag];
    else {
        int nl = max(s, q[num].l), nr = min(t, q[num].r);
        for(register int i = nl; i <= nr; i++)
            if(val[find(i)] - tag == q[num].x)
                q[num].ans++; 
    }
}
int main() {
    n = read(), m = read();
    for(register int i = 1; i <= n; i++) 
        a[i] = read();
    for(register int i = 1; i <= m; i++) {
        q[i].opt = read(), q[i].l = read(), q[i].r = read(), q[i].x = read();
    }
    L = sqrt(n);
    for(register int now = 1; now <= n; now += L) {
        memset(siz, 0, sizeof(siz));
        memset(rt, 0, sizeof(rt));
        s = now, t = min(n, now + L - 1);
        build();
        for(register int i = 1; i <= m; i++) {
            if(t < q[i].l || s > q[i].r) 
                continue;
            if(q[i].opt == 1) {
                if(q[i].l <= s && q[i].r >= t)
                    total_update(q[i].x);
                else
                    part_update(q[i].l, q[i].r, q[i].x);
            }
            else if(tag + q[i].x <= M) 
                query(i);
        }
    }
    for(register int i = 1; i <= m; i++)
        if(q[i].opt == 2)
            write(q[i].ans), putc('\n');
    flush();
    return 0;
} 
posted @ 2021-02-27 10:49  Terac  阅读(30)  评论(0)    收藏  举报  来源