yaoxi-std 的博客

$\text{开}\mathop{\text{卷}}\limits^{ju\check{a}n}\text{有益}$

0%

P4117 [Ynoi2018] 五彩斑斓的世界

P4117 [Ynoi2018] 五彩斑斓的世界

题面

题目链接

双倍经验

五彩斑斓的世界

五彩斑斓(JCY_ 的提交记录)

解法

Ynoi 难得不卡常的好题!思路也非常奇妙!

还像 第一分块 那样维护并查集数组。

考虑一个块内的最大值,设为 $mxn$。考虑一次修改 $x$。对于散块显然可以暴力重构。

对于一个整块:

  • 如果 $x \le mxn$,显然这个块不需要修改。
  • 如果 $x > \frac{mxn}{2}$,就将所有 $(x,mxn]$ 区间中的数减去 $x$。
  • 如果 $x \le \frac{mxn}{2}$,就讲所有 $[0,x]$ 区间中的数加上 $x$,并给整个块打上 $-x$ 的标记。

思考这种做法的复杂度,每次暴力将块中的 $x$ 种数字进行修改,都会使得块内的值域大小减去 $x$。而值域的大小是 $m=10^5$ 级别的,一共有 $\sqrt{n}$ 个块,所以总的修改次数是 $O(m \sqrt{n})$。

此外,这题空间 64MB,所以开不下 $O(m \sqrt{n})$ 的数组,只能开下 $O(n)$ 的。所以把询问离线下来,对每个块单独做询问,时间复杂度不变,空间复杂度降为 $O(n)$。

AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
* @file: P4117.cpp
* @author: yaoxi-std
* @url: https://www.luogu.com.cn/problem/P4117
*/
// #pragma GCC optimize ("O2")
// #pragma GCC optimize ("Ofast", "inline", "-ffast-math")
// #pragma GCC target ("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
#define resetIO(x) \
freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout)
#define debug(fmt, ...) \
fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
template <class _Tp>
inline _Tp& read(_Tp& x) {
bool sign = false; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) sign |= (ch == '-');
for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + (ch ^ 48);
return sign ? (x = -x) : x;
}
template <class _Tp>
inline void write(_Tp x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar((x % 10) ^ 48);
}
using ll = long long;
const int MAXN = 1e6 + 10, MAXM = 1e5 + 10, MAXQ = 5e5 + 10;
int n, m, q, blk, a[MAXN], fa[MAXN], siz[MAXM], val[MAXN], ans[MAXQ], repr[MAXM];
struct Query { int op, l, r, k, i; } qr[MAXQ];
inline int getfa(int x) { return fa[x] == x ? x : fa[x] = getfa(fa[x]); }
inline void change(int x, int y) {
if (!repr[x]) return;
if (repr[y]) {
fa[repr[x]] = repr[y];
siz[y] += siz[x];
siz[x] = repr[x] = 0;
} else {
val[repr[y] = repr[x]] = y;
siz[y] = siz[x];
siz[x] = repr[x] = 0;
}
}
inline void solve(int sl, int sr) {
int mxn = *max_element(a + sl, a + sr + 1), lzy = 0;
fill(siz, siz + m + 1, 0);
fill(val, val + m + 1, 0);
fill(repr, repr + m + 1, 0);
for (int i = sl; i <= sr; ++i) {
if (!repr[a[i]]) repr[a[i]] = fa[i] = i, val[i] = a[i], siz[a[i]] = 1;
else fa[i] = repr[a[i]], ++siz[a[i]];
}
for (int t = 1; t <= q; ++t) {
int l = qr[t].l, r = qr[t].r, k = qr[t].k;
if (r < sl || sr < l) continue;
if (qr[t].op == 1) {
if (mxn - lzy <= k) continue;
if (l <= sl && sr <= r) {
if (k > (mxn - lzy) / 2) {
for (int i = k + 1; i <= mxn - lzy; ++i) change(i + lzy, i - k + lzy);
mxn = min(mxn, k + lzy);
} else {
for (int i = k; i >= 0; --i) change(i + lzy, i + k + lzy);
lzy += k;
}
} else {
for (int i = sl; i <= sr; ++i) {
int w = val[getfa(i)]; a[i] = w - lzy, siz[w] = repr[w] = 0;
if (l <= i && i <= r && a[i] > k) a[i] -= k;
}
mxn = *max_element(a + sl, a + sr + 1), lzy = 0;
fill(val + sl, val + sr + 1, 0);
for (int i = sl; i <= sr; ++i) {
if (!repr[a[i]]) repr[a[i]] = fa[i] = i, val[i] = a[i], siz[a[i]] = 1;
else fa[i] = repr[a[i]], ++siz[a[i]];
}
}
} else {
if (k + lzy > m) continue;
if (l <= sl && sr <= r) {
ans[qr[t].i] += siz[k + lzy];
} else {
for (int i = sl; i <= sr; ++i) if (l <= i && i <= r) ans[qr[t].i] += val[getfa(i)] - lzy == k;
}
}
}
}
signed main() {
read(n), read(q), m = 1e5 + 1, blk = ceil(sqrt(n));
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1; i <= q; ++i) read(qr[i].op), read(qr[i].l), read(qr[i].r), read(qr[i].k), qr[i].i = i;
for (int i = 1; i <= n; i += blk) solve(i, min(n, i + blk - 1));
for (int i = 1; i <= q; ++i) if (qr[i].op == 2) write(ans[i]), putchar('\n');
return 0;
}