学习笔记:操作分块 / 根号重构

news/2025/9/19 9:14:31/文章来源:https://www.cnblogs.com/godmoo/p/19098970

感谢校内模拟赛给我强行灌输了这个东西。。。

概述

操作分块 / 根号重构,又名时间轴分块,可以解决需要多次修改和查询的问题,常常难以直接维护。

借鉴序列分块的思想,我们设定一个阈值 \(B\),将连续 \(B\) 次操作视为一块。考虑一次查询操作,将对它产生影响的修改分为两类:

  • 所在块之前的整块的修改;
  • 所在块中它之前的修改,仅有 \(\mathcal O(B)\) 次。

类似序列分块,整体维护一类修改的贡献,并暴力贡献二类修改。具体的,一个块的操作结束后,考虑暴力重构,将这一块算进整体贡献中,仅会重构 \(\mathcal O(\frac{q}{B})\) 次。

我们用几道例题来理解上述思想。

CF342E Xenia and Tree

题意: 给定一棵 \(n\) 个节点的树,初始时 \(1\) 号节点为红色,其余为蓝色。

\(q\) 次询问,两种操作:

  • 将一个节点变为红色;
  • 询问节点 \(u\) 到最近红色节点的距离。

\(1\le n, q \le 10^5\),5s

以阈值 \(B\) 操作分块,考虑一次查询 \(u\)

对于一个二类修改 \(v\),暴力贡献 \(dis(u, v)\),复杂度 \(\mathcal O(B)\)

对于整块的一类修改,我们需要在每个整块后进行重构。考虑以当前所有红点为起点集合进行多源 bfs,即可 \(\mathcal O(n)\) 求出每个节点 \(x\) 到最近红的的距离 \(f(x)\)\(\mathcal O(1)\) 贡献。

两部分贡献取 \(\min\) 即为答案。总复杂度 \(O(qB + q + n\cdot\frac{q}{B})\),视 \(n, q\) 同阶,取 \(B = n ^ {0.5}\),有最优复杂度 \(\mathcal O(n ^ {1.5})\),实际上取 \(B = 400\) 跑的并不快,开到 \(1000\) 就很好了。submission.

接下来看一道 DS。

[模拟赛] 浮夸(grandiloquence)

题意: 给定一棵初始 \(n\) 个节点的树,根节点为 \(1\)。再给定颜色数 \(k\),颜色的编号为 \([0, k)\),最开始所有点都没有被染色。

接下来进行以下三种操作共 \(m\)

  • 1 u c 对于 \(u\) 子树内的节点 \(v\),将 \(v\) 染色为 \((c + dis(u, v)) \bmod k\)
  • 2 u c 询问 \(u\) 子树内颜色为 \(c\) 的节点个数;
  • 3 u \(~~~\)插入一个编号为 \(\lvert V\rvert + 1\) 的节点,其父亲为 \(u\),没有被染色。其中 \(V\) 是当前点集。

\(1\le n, m \le 2\times10^5\)\(0\le c \lt k \le 10\),强制在线。

假设没有操作 \(3\),直接用 dfs 序拍平,转换成区间操作。颜色数很少,直接暴力维护。观察 \(1\) 操作,颜色可以拆成 \((c - dep_u + dep_v)\),考虑区间打 \((c - dep_u)\) 的标记,按 depcol 统计即可。线段树随便维护。

有操作 \(3\) 的话,考虑操作分块,每次操作 \(3\)\(B\) 个,就新开一块,对于 \(1,2\) 操作,分两类:

  • \(u\) 不是块内新增节点(即二类修改):算完前面的整块节点后,枚举所有块二类修改,若其在 \(u\) 子树内,则暴力计算贡献,\(\mathcal O(B)\),这是平凡的;
  • \(u\) 是块内新增节点:那么 \(u\) 子树内均为新增节点,于是 \(siz_u \lt B\),直接暴力 dfs 算答案,\(\mathcal O(B)\)

\(B\) 个后无重建线段树。话说为啥大家都写的分块啊。

再来一道?

[模拟赛] 序列查询(query)

题意: 给定一长为 \(n\) 的非负整数序列 \(a\),你需要支持以下两种操作共 \(q\) 次:

  • 区间翻转;
  • 区间询问 \(\text{mex}\)

\(1 \le n, q \le 2 \times 10 ^ 5\),4s

以阈值 \(B\) 操作分块,单独考察每一块操作。

先对序列按受操作情况划分颜色段,每个颜色段受到的操作相同。操作均为区间操作,最多使其头、尾所在颜色段分裂,增加 \(\mathcal O(1)\) 个颜色段,于是颜色段仅有 \(\mathcal O(B)\) 个。ODT 维护,翻转可以考虑暴力在 ODT 上翻转并打上翻转标记,单次翻转复杂度 \(\mathcal O(B)\)

询问 \(\text{mex}\) 很不好维护,考虑离线下来枚举答案。具体的,维护出每个颜色段受到的询问的集合,离线下来遍历 ODT,算出包含 \(x\) 的询问的集合 \(S_x\)压位存储。再枚举 \(\text{mex}\),枚举到 \(i\) 时,还没确定答案且属于 \(S_i\) 的询问的答案即为 \(i\)

重构直接遍历 ODT 进行重构即可。实现上,为了让 ODT 支持翻转,直接用 std::vector 存储,定位时暴力往后找,复杂度即为颜色段个数 \(\mathcal O(B)\);对于插入,我们选择相信 insert 的超小常数(雾

复杂度分析非常困难,在以下条件下:

  • 压位写法,视位运算复杂度为 \(O(1)\)
  • \(n, q\) 同阶
  • insert 复杂度 \(\mathcal O(1)\)

,复杂度为 \(O(\frac{n ^ 2}{B})\),若使用 __uint128_t 进行压位,令 \(B = w = 128\),复杂度 \(O(\frac{n ^ 2}{w})\)

P5443 [APIO2019] 桥梁

题意: 给定一张 \(n\)\(m\) 边的无向图,第 \(i\) 条边带权 \(w_i\),有 \(q\) 次如下两种操作:

  • \(w_x \gets y\)
  • 查询从 \(x\) 开始,只通过 \(\ge y\) 的边,可达点数量。

\(1 \le n \le 5\times10^4\)\(0 \le m \le 10^5\)\(1 \le q \le 10^5\)\(w_i, y \le 10^9\).

若没有修改操作,可以使用 Kruskal 重构树,也可以离线,将询问按 \(y\) 从大到小排序,并查集维护即可。

有修改,不妨考虑操作分块,将 \(B\) 次操作划为一块。Kruskal 重构树的形态受修改操作影响,难以动态维护,于是我们考虑并查集,随之而来的问题是:我们怎么同时处理时间轴边权序

对于前面的块的修改,对每一条边维护出其最后一次修改,首先肯定是以边权序对询问排序,那么对块内无修改的边,它最后一次修改在当前块前,直接随边权序加入并查集即可。

再考虑块内有修改(二类修改)的边:仅有 \(B\) 条,考虑可撤销并查集,对于询问 \((t, x, y)\),加入满足以下条件的边 \((t_0, x_0, y_0)\)

  • \(y_0 \ge y\)
  • 是满足 \(t_0 \lt t\) 之前对 \(x_0\) 的最后一次修改。

,算出答案后再撤销即可,这部分单次询问是 \(\mathcal O(B)\) 的。

由于要支持撤销,并查集不能路径压缩,使用启发式合并,总复杂度 \(\mathcal O((m\log m + B\log B)\cdot\frac{q}{B} + qB\log n)\),视 \(n, m, q\) 同阶,取 \(B = q^{0.5}\) 时,有最优复杂度 \(\mathcal O(q\log n\sqrt{n})\),实测块长取 \(900\) 为最优,给出实现:

const int N = 1e5 + 5, B = 900;
int n, m, q, rk[N], vis[N], ans[N];
struct Edge{ int u, v, w, id; } E[N];
struct Query{ int t, x, y; };
vector<Query> M, Q, V;namespace DSU {int fa[N], siz[N], stk[N], tp, cur;void init(){ for(int i = 1; i <= n; i++) siz[fa[i] = i] = 1; tp = 0; }int find(int u){ return u == fa[u] ? u : find(fa[u]); }void merge(int u, int v){if((u = find(u)) != (v = find(v))){if(siz[u] > siz[v]) swap(u, v);fa[u] = v, siz[v] += siz[u], stk[++tp] = u;}}void rec(){ cur = tp; }void undo(){while(tp > cur){int u = stk[tp];siz[fa[u]] -= siz[u], fa[u] = u, tp--;}}
} using namespace DSU;void solve(){sort(E + 1, E + m + 1, [](Edge x, Edge y){ return x.w > y.w; });for(int i = 1; i <= m; i++) rk[E[i].id] = i;sort(all(Q), [](Query x, Query y){ return x.y > y.y; });for(int i = 1; i <= m; i++) vis[i] = -1; // 无修改for(auto [t, x, y] : M) vis[rk[x]] = 0; // 有修改V = M; for(auto [t, x, y] : M) V.eb((Query){0, x, E[rk[x]].w});sort(all(V), [](Query x, Query y){ return x.t < y.t; });init();int pos = 1;for(auto [t, x, y] : Q){while(pos <= m && E[pos].w >= y){ // 无修改无需考虑撤销if(!~vis[pos]) merge(E[pos].u, E[pos].v);pos++;}rec();for(auto [_t, _x, _y] : V){ // 算上次修改时间if(_t > t) break;vis[rk[_x]] = _t;}for(auto [_t, _x, _y] : V){if(_t > t) break;if(_t < vis[rk[_x]]) continue; // 不是最新修改if(_y >= y) merge(E[rk[_x]].u, E[rk[_x]].v);}ans[t] = siz[find(x)], undo();}for(auto [t, x, y] : V) E[rk[x]].w = y;
}int main() {ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);for(int i = (cin >> n >> m, 1); i <= m; i++)cin >> E[i].u >> E[i].v >> E[i].w, E[i].id = i;for(int i = (cin >> q, 1), op, x, y; i <= q; i++){cin >> op >> x >> y, op == 1 ? M.eb((Query){i, x, y}) : Q.eb((Query){i, x, y});if(M.size() + Q.size() >= B) solve(), M.resize(0), Q.resize(0);} if(Q.size()) solve();for(int i = 1; i <= q; i++) if(ans[i]) cout << ans[i] << '\n';cout << flush;return 0;
}

发现这题与前面其他题有所不同,我们貌似没有很快速的处理出前面整块的贡献,而是跟着当前块的修改一起贡献。所以操作分块不一定都需要将前面整块的贡献插到一个全局的 DS 中,由此也可以看出操作分块有很强的扩展性。

完结撒花~ ★,°:.☆( ̄▽ ̄)/$:.°★

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/907641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

url测试脚本2

!/bin/sh . /etc/init.d/functions 待检测的 URL 列表 array=( "http://blog.oldboyedu.com" "http://blog.etiantian.org" "http://oldboy.blog.51cto.com" "http://10.0.0.7&quo…

运动控制教学——5分钟学会机器人运动学! - 实践

运动控制教学——5分钟学会机器人运动学! - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

url测试脚本3

!/bin/sh . /etc/init.d/functions 待检测的 URL 列表 array=( "http://mail.163.com" "http://mail.sina.com/" ) 等待效果,输出进度 wait_for_start() { echo -n "Start Curl_check"…

深入解析:linux基本知识

深入解析:linux基本知识pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &q…

解决方案架构师是做什么

解决方案架构师 面试题 客户是怎么管理的 渠道变革变换的是哪些内容。变的是什么? 分层分级是怎么设计,价格体系是怎么制定的 marking 是怎么做的? CAP模型,是怎么管理的, 营销活动和销售是如何结合的,IT解决方案…

鸿蒙应用开发从入门到实战(九):ArkTS渲染控制

ArkTS拓展了TypeScript,可以结合ArkUI进行渲染控制,是的界面设计具有可编程性。本文简要描述鸿蒙应用开发中的条件渲染和循环渲染。大家好,我是潘Sir,持续分享IT技术,帮你少走弯路。《鸿蒙应用开发从入门到项目实…

C# 2025年6-9月TIOBE排名增长及未来展望

根据 TIOBE 编程语言排行榜 2025 年 6 月至 9 月的公开数据,C# 的排名和市场份额变化如下(综合多个月份数据整理):一、 C# 在 2025 年 TIOBE 排行榜的连续增长趋势2025 年 6 月排名:第 5 位市场份额:4.69%2025 年…

一个基于 .NET 开源、简易、轻量级的进销存管理系统

前言 最近有小伙伴在后台留言问:.NET 有值得推荐学习的进销存管理系统吗?今天大姚给大家推荐一个基于 .NET 开源、简易、轻量级的进销存管理系统:JxcLite。 项目介绍 JxcLite 是一个基于 Known 框架开发(基于 .NET…

Java 接口详解

Java 接口详解接口(Interface)是 Java 中实现抽象、定义规范、支持多态的核心机制,也是面向对象编程(OOP)中 “封装、继承、多态” 三大特性的重要载体。它不仅是代码层面的语法结构,更体现了 “面向接口编程” …

飞算 JavaAI 启用体验全解析

飞算 JavaAI 启用体验全解析2025-09-19 09:03 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important;…

采用tree命令导出文件夹/文件的目录树(linux)

采用tree命令导出文件夹/文件的目录树(linux)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &…

MySQL 字符集详解

MySQL 字符集详解在 MySQL 数据库中,字符集(Character Set)是决定数据如何存储、传输和显示的核心组件,直接影响数据一致性(如避免乱码)、查询性能和多语言支持能力。本文将从字符集的基础概念出发,深入讲解 My…

The 2024 ICPC Asia East Continent Online Contest (I) 4/12 A/F/G/M

M. Find the Easiest Problem 签到题,直接模拟即可点击查看代码 #include<bits/stdc++.h> #define int long long using namespace std; using pii=pair<int,int>; using ll = long long; using ull = un…

Python上课

Python上课题目 杨辉三角 99 乘法表

Yapi接口文档本地安装

Yapi接口文档本地安装t

深入解析 JVM 类加载机制:从字节码到运行时对象

一、概述:为什么需要类加载? Java 语言的核心特性之一是"一次编写,到处运行",这背后的关键在于 Java 虚拟机(JVM)和其类加载机制。当我们编写好 Java 代码并将其编译为 .class 字节码文件后,这些静态…

博弈论学习(第二天)

博弈的基本理性假设: 一般来说,对于研究博弈问题,需要假设参与者具有完美理性,这分三方面,第一个就是参与者的偏好要有一定性,比如对风险的偏好,不能说一个参与者做第一个决策时属于风险接受型,而做第二个决策…

PHP 和 Elasticsearch:给你的应用加个强力搜索引擎

PHP 和 Elasticsearch:给你的应用加个强力搜索引擎 现在做 Web 应用,搜索功能基本是标配。不管你做电商、CMS 还是社交应用,用户都希望搜索又快又准。如果你用 PHP 开发,肯定遇到过数据库搜索的瓶颈——数据一多就…

深入解析:SSH带外管理

深入解析:SSH带外管理2025-09-19 08:42 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font…

Windows 系统部署 Mosquitto MQTT broker 完整指南

一、前言/介绍简要介绍 Mosquitto 是什么(轻量级、开源 MQTT 消息代理)MQTT 协议的应用场景(IoT、移动应用、消息推送等)本文目标:在 Windows 系统上快速搭建一个可用的 MQTT 服务器二、下载与安装markdown 复制…