ACM 数据结构与算法思想记录

news/2026/1/19 21:37:52/文章来源:https://www.cnblogs.com/cqbzlzh/p/19503797

老年 ACMer 尝试对抗阿尔茨海默病(

图论

DFS序 \(O(n \log n)\) - \(O(1)\) Lca

考虑点\(u\)\(v\) 及其 \(Lca\)\(l\),不妨设 $dfn_u \lt dfn_v $,那么有 \(dfs\) 序从 \(l\)\(u\) 递增,此后回到 \(l\) 后再向着 \(v\)递增

所以,按\(dfs\)序构成的序列中,\(dfn_u\)\(dfn_v\)这一段中深度最小的点一定是\(l\)\(v\)方向直接连接的节点

所以可以直接使用\(ST\)表维护父节点\(dfs\)序的最小值即可

注意可能有\(u\)\(v\)的祖先的情况,因此\(ST\)表询问区间该为\([dfn_u+1,dfn_v]\)进行规避

P3379 【模板】最近公共祖先(LCA)

点击查看代码
int mindfn(int x, int y) {if (dfn[x] < dfn[y]) return x;return y;
}struct ST {int st[MAXN][25];void init(int n) {int t = std::__lg(n) + 1;for (int i = 1; i <= n; i++) st[dfn[i]][0] = fa[i];for (int j = 1; j <= t; j++) {for (int i = 1; i  + (1 << (j - 1)) <= n; i++) {st[i][j] = mindfn(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);}}}int query(int l, int r) {int t = std::__lg(r - l + 1);return mindfn(st[l][t], st[r - (1 << t) + 1][t]);}
}st;auto Lca = [&](int x, int y) {if (dfn[x] > dfn[y]) std::swap(x, y);return st.query(dfn[x] + 1, dfn[y]);   
};

Dsu on tree

当涉及子树问题时,对于父亲相同的子树,如果在计算一个的答案时保留了其他子树的信息,统计会出现混乱。但是这种保留对于父亲子树的统计有利

于是考虑对于每个子树,先计算它轻儿子子树中的答案,并且不保留。最后计算重儿子的答案并保留,将轻儿子的答案往上合并

对于每个节点,每当其到根节点有一条轻链时会被暴力合并一次,由树链剖分的性质可知最多会被合并 \(O(\log n)\)

CF600E Lomsat gelral

点击查看代码
#include <bits/stdc++.h>#define ll long longconst int MAXN = 1e5 + 5;int n, a[MAXN], fa[MAXN], siz[MAXN], son[MAXN];
std::vector <int> G[MAXN];
ll ans[MAXN];
int buc[MAXN], mx, S;
ll sum;int main() {std::ios::sync_with_stdio(0);std::cin.tie(0);std::cout.tie(0);int n;std::cin >> n;for (int i = 1; i <= n; i++) std::cin >> a[i];for (int i = 1;  i < n; i++) {int u, v;std::cin >> u >> v;G[u].push_back(v);G[v].push_back(u);}auto dfs1 = [&](int u, int f, auto self) -> void {fa[u] = f; siz[u] = 1;for (const auto &v : G[u]) {if (v == f) continue;self(v, u, self);siz[u] += siz[v];if (siz[v] > siz[son[u]]) son[u] = v;}};dfs1(1, 0, dfs1);auto calc = [&](int u, int delta, auto self) -> void {buc[a[u]] += delta;if (buc[a[u]] > mx) {mx = buc[a[u]];sum = a[u];}else if (buc[a[u]] == mx) {sum += a[u];}for (const auto &v : G[u]) {if (v == fa[u] || v == S) continue;self(v, delta, self);}};auto dfs2 = [&](int u, bool keep, auto self) -> void {for (const auto &v : G[u]) {if (v == fa[u] || v == son[u]) continue;self(v, 0, self);}if (son[u]) {self(son[u], 1, self);S = son[u];}calc(u, 1, calc);ans[u] = sum;S = 0;if (!keep) {calc(u, -1, calc);mx = 0; sum = 0;}};dfs2(1, 1, dfs2);for (int i = 1; i <= n; i++) {std::cout << ans[i] << " "; }std::cout << '\n';
}

点分治

对于树上的路径问题,我们考虑固定一个根的情况,答案分为两种:跨过根的路径(包括以根为端点)和根的子树内部的路径

显然后者有子问题结构,考虑分治。当每次的根都取子树重心时,有最少递归层数 \(O(\log n)\)

由于较小的递归层数,我们可以每一层都采取相对暴力的做法来统计跨过根的路径

注意每次向下分治时重新计算子树大小,不然会导致重心求解出错,时间复杂度假掉

P3806 【模板】点分治

点击查看代码
#include <bits/stdc++.h>const int MAXN = 1e4 + 5;std::vector <std::pair <int, int> > G[MAXN];
int qry[105];
bool ans[105];
std::bitset <10000005> exist;int main() {int n, q;std::cin >> n >> q;for (int i = 1; i < n; i++) {int u, v, w;std::cin >> u >> v >> w;G[u].push_back({v, w});G[v].push_back({u, w});}for (int i = 1; i <= q; i++) {std::cin >> qry[i];}std::vector <bool> vis(n + 1);std::vector <int> mx(n + 1), siz(n + 1);int rt;int node_cnt;auto Find_size = [&](int u, int f, auto self) -> void {siz[u] = 1;for (const auto &p : G[u]) {int v = p.first, w = p.second;if (v == f || vis[v]) continue;self(v, u, self);siz[u] += siz[v];}};auto Find_cent = [&](int u, int f, auto self) -> void {mx[u] = 0;for (const auto &p : G[u]) {int v = p.first, w = p.second;if (vis[v] || v == f) continue;self(v, u, self);mx[u] = std::max(mx[u], siz[v]);}mx[u] = std::max(mx[u], node_cnt - siz[u]);if (mx[u] < mx[rt]) rt = u;};std::vector <int> dis;auto Find_dis = [&](int u, int f, int d, auto self) -> void {if (d > 10000000) return;dis.push_back(d);for (const auto & p : G[u]) {int v = p.first, w = p.second;if (v == f || vis[v]) continue;self(v, u, d + w, self);}};std::vector <int> used;auto calc = [&](int u, int d, auto self) -> void {Find_dis(u, u, d, Find_dis);for (const auto &x : dis) {for (int i = 1; i <= q; i++) {if (qry[i] - x >= 0) ans[i] |= exist[qry[i] - x];}}for (const auto &x : dis) {exist[x] = 1;used.push_back(x);}dis.clear();};auto solve = [&](int u, auto self) -> void {vis[u] = 1; exist[0] = 1;for (const auto &p : G[u]) {int v = p.first, w = p.second;if (vis[v]) continue;calc(v, w, calc);} for (const auto &x : used) exist[x] = 0;used.clear();for (const auto &p : G[u]) {int v = p.first;if (vis[v]) continue;mx[rt = 0] = 0x3f3f3f3f;Find_size(v, u, Find_size);node_cnt = siz[v];Find_cent(v, u, Find_cent);self(rt, self);}};mx[rt = 0] = 0x3f3f3f3f;node_cnt = n;Find_size(1, 0, Find_size);Find_cent(1, 0, Find_cent);solve(rt, solve);for (int i = 1; i <= q; i++) {if (ans[i]) {std::cout << "AYE" << '\n';}else std::cout << "NAY" << '\n';}
}

点分树

考虑利用点分治只会递归\(\log n\)层的优秀性质,每次递归的时候,子树的重心向当前根节点连边,就形成了一颗\(\log n\)的树

这棵树满足对于\(l = Lca(u, v)\),必有 \(l\) 在原树 \(u\)\(v\)的路径上

因此可以从在点分树上从\(u\)开始往上跳,假设当前跳到了\(l\),那么对\(l\)除去\(u\)方向的子树的所有节点,都有一条在原树中经过\(l\)到达\(u\)的路径,可以采用数据结构进行统计

树高的性质保证了最多向上跳 \(\log n\) 次,时间复杂度正确

P6329 【模板】点分树 / 震波

点击查看代码
#include <bits/stdc++.h>const int MAXN = 1e5 + 5;int n, m, a[MAXN];
std::vector <int> G[MAXN];struct Sgt {struct Node {int ls, rs, sum;}t[MAXN * 80];int cnt;void update(int &p, int l, int r, int pos, int val) {if (!p) p = ++cnt;t[p].sum += val;if (l == r) return;int mid = (l + r) >> 1;if (pos <= mid) update(t[p].ls, l, mid, pos, val);else update(t[p].rs, mid + 1, r, pos, val);}int query(int p, int l, int r, int ql, int qr) {if (!p || ql > qr) return 0;if (ql <= l && r <= qr) return t[p].sum;int mid = (l + r) >> 1, ret = 0;if (ql <= mid) ret += query(t[p].ls, l, mid, ql, qr);if (qr > mid) ret += query(t[p].rs, mid + 1, r, ql, qr);return ret;}
}t1, t2;
int rt1[MAXN], rt2[MAXN];int lst;
int dfn[MAXN], fa[MAXN], dcnt, dep[MAXN];void dfs(int u, int f) {fa[u] = f; dfn[u] = ++dcnt; dep[u] = dep[f] + 1;for (const auto &v : G[u]) {if (v == f) continue;dfs(v, u);}
}struct ST {int st[MAXN][25];int dfnmin(int x, int y) {if (dfn[x] < dfn[y]) return x;return y;}void init() {int t = std::__lg(n) + 1;for (int i = 1; i <= n; i++) st[dfn[i]][0] = fa[i];for (int j = 1; j <= t; j++) {for (int i = 1; i + (1 << (j - 1)) <= n; i++) {st[i][j] = dfnmin(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);}}}int query(int l, int r) {int t = std::__lg(r - l + 1);return dfnmin(st[l][t], st[r - (1 << t) + 1][t]);}
}st;int Lca(int x, int y) {if (x == y) return x;if (dfn[x] > dfn[y]) std::swap(x, y);return st.query(dfn[x] + 1, dfn[y]);
}int dis(int x, int y) {int l = Lca(x, y);return dep[x] + dep[y] - 2 * dep[l];
}int fa2[MAXN], mx[MAXN], siz[MAXN];int main() {std::ios::sync_with_stdio(0);std::cin.tie(0);std::cout.tie(0);std::cin >> n >> m;for (int i = 1; i <= n; i++) std::cin >> a[i];for (int i = 1; i < n; i++) {int u, v;std::cin >> u >> v;G[u].push_back(v);G[v].push_back(u);}dfs(1, 0);st.init();std::vector <bool> vis(n + 1);int node_cnt, rt;auto Find_cent = [&](int u, int f, auto self) -> void {mx[u] = 0;for (const auto &v : G[u]) {if (vis[v] || v == f) continue;self(v, u, self);mx[u] = std::max(mx[u], siz[v]);}mx[u] = std::max(mx[u], node_cnt - siz[u]);if (mx[rt] > mx[u]) rt = u;};auto Find_size = [&](int u, int f, auto self) -> void {siz[u] = 1;for (const auto &v : G[u]) {if (vis[v] || v == f) continue;self(v, u, self);siz[u] += siz[v];}};auto build = [&](int u, auto self) -> void {vis[u] = 1; Find_size(u, -1, Find_size);for (const auto &v : G[u]) {if (vis[v]) continue;mx[rt = 0] = 0x3f3f3f3f;node_cnt = siz[v];Find_cent(v, u, Find_cent);fa2[rt] = u;self(rt, self);}};mx[rt = 0] = 0x3f3f3f3f;node_cnt = n;Find_cent(1, 0, Find_cent);fa2[rt] = 0;build(rt, build);auto query = [&](int x, int k) {int f = fa2[x], cur = x, ret = t1.query(rt1[x], 0, n, 0, k);while (f) {int d = k - dis(x, f);ret += (t1.query(rt1[f], 0, n, 0, d) - t2.query(rt2[cur], 0, n, 0, d));cur = fa2[cur];f = fa2[f];}return ret;};auto update = [&](int x, int y, bool flag) {int cur = x;while (cur) {if (flag) t1.update(rt1[cur], 0, n, dis(x, cur), -a[x]);t1.update(rt1[cur], 0, n, dis(x, cur), y);int f = fa2[cur];if (f) {if (flag) t2.update(rt2[cur], 0, n, dis(x, f), -a[x]);t2.update(rt2[cur], 0, n, dis(x, f), y);}cur = f;}};for (int i = 1; i <= n; i++) {update(i, a[i], 0);}while (m--) {int op, x, y;std::cin >> op >> x >> y;x ^= lst; y ^= lst;if (op == 0) {std::cout << (lst = query(x, y)) << '\n';} if (op == 1) {update(x, y, 1);a[x] = y;}}return 0;
}

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

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

相关文章

“推三返一”裂变模型深度拆解:如何让用户从消费者变为增长合伙人?

在流量红利见顶的当下&#xff0c;一种名为“推三返一”的商业模式&#xff0c;正成为许多高客单价品牌私域增长的秘密引擎。一、核心机制&#xff1a;三级返利驱动的心理闭环该模型构建了一个清晰的“消费-推广-回本”路径&#xff1a;身份转化&#xff1a;用户通过单次消费&a…

微信小程序毕设项目:基于nodejs的大众点评美食版小程序(源码+文档,讲解、调试运行,定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

苹果OLED MacBook Pro或提前发布,三星屏幕已量产

一项新传言表明&#xff0c;苹果合作伙伴三星显示器已提前开始为新款MacBook Pro量产OLED屏幕。这一传言来自韩国Naver平台的yeux1122账户&#xff0c;由于该消息源的可靠性记录并不完美&#xff0c;需要谨慎对待。不过&#xff0c;这一时间线大致与彭博社马克古尔曼等更可靠消…

华为鸿蒙应用开发者基础认证

现在国产操作系统越来越火&#xff0c;鸿蒙现在由工信部牵头拉了14家单位组成鸿蒙“国家队”&#xff0c;这生态直接升级成国家层面的布局。到2025年底&#xff0c;鸿蒙设备都破11亿台了&#xff0c;开发者突破900多万人。但问题也很明显目前能用的核心应用不够多&#xff0c;懂…

第一性原理计算方法及应用

材料基因工程是近年来国际材料领域兴起的颠覆性前沿技术&#xff0c;随着国内计算机技术的快速发展&#xff0c;多尺度材料模拟计算成为材料研究中不可或缺的一部分。计算材料学主要致力于建立可预测或可描述的模型&#xff0c;以指导实验研究,可以减少实验试错次数和降低成本,…

小程序计算机毕设之基于nodejs的演唱会路演活动报名小程序的设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

高效安全的数字化工作空间:VDI桌面云与私有化部署的价值解析

在数字化转型不断深化的今天&#xff0c;如何构建一个兼具高效协作、集中管控与本质安全的工作环境&#xff0c;已成为众多机构&#xff0c;特别是对数据安全与业务流程稳定性有高要求的领域&#xff0c;亟待解决的核心课题。传统的分散式个人计算机管理模式&#xff0c;日益面…

【毕业设计】基于nodejs的大众点评美食版小程序(源码+文档+远程调试,全bao定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

当AI重塑数据价值曲线,存储底座正在被重新定义

作者&#xff1a;王聪彬过去两年&#xff0c;机械硬盘在生成式AI时代展现出了强劲的活力与广阔的潜力。 自1956年问世以来&#xff0c;机械硬盘&#xff08;HDD&#xff09;长期是计算机存储的核心。从最初几MB到TB级别&#xff0c;从几千转到上万转&#xff0c;每一次技术突破…

微型导轨精度等级如何检测?

微型导轨为小型精密传动元件&#xff0c;常用于需高精度机械传动系统场景中&#xff0c;如医疗器械、半导体设备、机器人、光学仪器等领域。其精度等级直接影响设备性能。从普通级到超精密级&#xff0c;不同场景对直线度、平行度及定位精度的要求各异。那么&#xff0c;大家知…

五度易链企业数据服务架构思考:从“存数据”到“用数据”的全周期解决方案

我们曾以为&#xff0c;将企业经营变成数字&#xff0c;就是大数据的终点。但当每一家竞争对手、每一个供应链环节、甚至每一则市场情绪都在同步数字化时&#xff0c;一个更尖锐的问题浮出水面&#xff1a;当透明成为一种常态&#xff0c;你的信息优势&#xff0c;究竟还能藏在…

实时云渲染:重塑数字体验的技术革新

在数字化浪潮席卷全球的今天&#xff0c;高质量、高沉浸感的视觉内容已成为各行业竞争的关键要素。从互动娱乐、工业仿真到虚拟会展、数字孪生&#xff0c;对图形处理能力的需求日益增长。然而&#xff0c;本地硬件性能瓶颈、高昂的终端投入与复杂的运维管理&#xff0c;让许多…

如何让大模型实现复杂、精准的推理与规划?

大模型的复杂推理与精准规划能力&#xff0c;是其从“生成内容”向“解决问题”升级的核心&#xff0c;需兼顾底层模型能力打磨、上层交互引导、跨技术工具协同及全流程迭代优化。以下是系统化实现路径&#xff0c;兼顾理论逻辑与实操落地&#xff0c;确保推理深度、准确性与可…

野路子编问卷 VS 科学设计?宏智树 AI 让实证数据告别 “无效废纸”

还在凭感觉编问卷题目&#xff0c;结果回收的数据信效度全不达标&#xff1f;还在因为 “一题多问”“选项交叉” 被导师打回反复修改&#xff1f;还在对着一堆杂乱数据无从下手&#xff0c;让实证研究沦为 “空谈”&#xff1f;作为深耕论文写作科普的教育博主&#xff0c;后台…

丑数 II:一题看穿你是“暴力选手”,还是“结构化思维玩家”

丑数 II:一题看穿你是“暴力选手”,还是“结构化思维玩家” 一、引子:为什么一道“看似简单”的题,这么多人写不对? 题目很简单: 丑数:只包含质因子 2、3、5 的正整数 求第 n 个丑数 很多人第一反应是: “不就是判断一个数能不能被 2 / 3 / 5 除干净吗?” 于是写出类…

人群仿真软件:AnyLogic_(5).行为和交互规则定义

行为和交互规则定义 在人群仿真软件中&#xff0c;定义行为和交互规则是至关重要的一步。这些规则决定了模拟中个体如何移动、如何决策以及如何与其他个体和环境互动。在AnyLogic中&#xff0c;行为和交互规则可以通过多种方式定义&#xff0c;包括使用内置的行人库&#xff0…

数据不会 “说话”?宏智树 AI:一键解锁论文实证分析的通关密码

还在对着一堆问卷、实验数据犯愁&#xff1f;收集了上百份调研问卷却只会做简单计数&#xff0c;跑了几十组实验数据却挖不出核心规律&#xff0c;好不容易算出结果&#xff0c;又不知道怎么转化为严谨的学术论证&#xff1f;作为深耕论文写作科普的博主&#xff0c;我发现宏智…

LLM兽医牙科影像实时诊断提速

&#x1f4dd; 博客主页&#xff1a;Jax的CSDN主页 兽医牙科影像智能诊断&#xff1a;实时提速的创新实践 目录引言&#xff1a;兽医牙科的隐性危机与技术契机 一、兽医牙科影像诊断的现状与核心痛点 二、LLM赋能兽医牙科影像诊断的技术路径 1. 自然语言理解&#xff1a;从影像…

开题报告写作零门槛!宏智树 AI 教你避开 90% 的导师驳回坑

作为深耕论文写作科普的教育博主&#xff0c;后台总能收到大量同学的求助&#xff1a;“开题报告改了 N 版还被导师打回”“选题太宽泛&#xff0c;根本不知道怎么聚焦”“文献综述写得像流水账&#xff0c;理不清研究脉络”…… 其实&#xff0c;开题报告不是 “憋出来” 的&a…

云交互:开启数字体验的全新时代

在数字化浪潮的推动下&#xff0c;我们与信息、服务乃至世界的交互方式正在经历一场静默而深刻的变革。“云交互”这一概念&#xff0c;已从技术前沿的理念&#xff0c;逐步渗透成为支撑未来数字生活与工作的核心模式。它不仅仅是将计算任务从本地迁移到远端&#xff0c;更是代…