题解 P4117 [Ynoi2018] 五彩斑斓的世界
update on 2021.11.12 修正了一些错误。
题意
给定一个长为
- 将
中大于 的数减去 。 - 查询区间
中等于 的数的个数。
数据范围:
题解
设
对于每个块内,记录每一个值出现的次数,维护一个最大值
对于修改操作,散块直接暴力重构。
若修改一个整块,分类讨论一下,设更新所有值为
-
若
,把块内所有大于 的数减去 。此时需更新的区间为 ,此时用 的代价使 减少了 。 -
若
,把块内所有小于等于 的数加上 ,再打上区间减 的标记,需更新的区间为 ,此时用 的代价使 的代价减少了 。
首先所有的值会越来越小,又
散块查询暴力统计,整块查询返回块内值
空间复杂度为即为
考虑怎么更新每个值对应的一堆数。
比较好写的写法是并查集,对于任意一个其值为
在合并的时候,设把值为
- 若
不存在,则把 全放到 上,并使 指向 。 - 若
存在,则直接合并 和 两个并查集。
用这种并查集可把维护每个块的复杂度优化到了
此题卡了
有个逐块处理的 trick,即离线下来一块一块处理,可把空间复杂度优化到
然而我常数大!!!!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;
}
分类:
题解
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战