题解 P4117 [Ynoi2018] 五彩斑斓的世界

link

update on 2021.11.12 修正了一些错误。

题意

给定一个长为 n 的序列 am 次操作:

  • al,,r 中大于 x 的数减去 x
  • 查询区间 al,,r 中等于 x 的数的个数。

数据范围:1n105,1m5×105,0ai,x105

题解

w 为值域。

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

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

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

  • maxn2x,把块内所有大于 x 的数减去 x。此时需更新的区间为 [x+1,maxn],此时用 O(maxnx)×O(ds) 的代价使 maxn 减少了 maxnx

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

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

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

空间复杂度为即为 O(wn)w 为值域。

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

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

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

  • rty 不存在,则把 rtx 全放到 rty 上,并使 rty 指向 y
  • rtx 存在,则直接合并 rtxrty 两个并查集。

用这种并查集可把维护每个块的复杂度优化到了 O(α(n))O(1),总体时间复杂度是 O((n+m)n),空间复杂度为 O(wn)

此题卡了 O(wn) 空间。

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

然而我常数大!!!!1 还要卡常才能过。

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

#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 @   Terac  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示