浅谈dsu on tree

news/2025/9/27 11:06:23/文章来源:https://www.cnblogs.com/2024Xycx/p/19114854

前言

先学树剖。

讲讲启发式合并,最经典的就是并查集的按秩合并,这里不细讲。

常用的启发式合并就是小集合合并到大集合上,复杂度从 \(O(n^2)\) 优化至 \(O(n \log n)\)

例题

P3201 [HNOI2009] 梦幻布丁

题目描述

\(n\) 个布丁摆成一行,进行 \(m\) 次操作。每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。

例如,颜色分别为 \(1,2,2,1\) 的四个布丁一共有 \(3\) 段颜色.

set或链表的启发式合并板。

考虑没有修改怎么做,显然统计一下有多少个 \(color[i]\ne color[i-1]\) 即可,复杂度 \(O(n)\)

考虑如何暴力修改,用set或链表维护每个颜色,修改时判断一下一个位置两边是不是要变的颜色,如果是 \(ans--\)

我们发现 \(x\)\(y\)\(y\)\(x\) 对答案没有影响,所以用 \(siz\) 小的链表合并到大的上面,复杂度 \(O(n \log n)\)

code

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N = 1e6 + 5;
int n, q, a[N];
int head[N], nxt[N], siz[N];
int ans;
int f[N * 10];inline void merge(int &x, int &y) {if (siz[x] > siz[y]) swap(x, y);if (!siz[x] || x == y) return ;for (int i = head[x]; i; i = nxt[i]) ans -= (a[i - 1] == y) + (a[i + 1] == y);for (int i = head[x]; i; i = nxt[i]) {a[i] = y;if (nxt[i] == 0) {nxt[i] = head[y];head[y] = head[x];break;}}siz[y] += siz[x];siz[x] = 0;head[x] = 0;
}signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> n >> q;for (int i = 1; i < N * 10; i++) f[i] = i;ans = 1;for (int i = 1; i <= n; i++) { cin >> a[i];nxt[i] = head[a[i]];head[a[i]] = i;siz[a[i]]++;if (nxt[i] != i - 1) ans++;}	int opt, x, y;while (q--) {cin >> opt;if (opt == 1) {cin >> x >> y;merge(f[x], f[y]);} else {cout << ans << endl;}}return 0;
}

dsu on tree

算法简介

考虑这样的一类树上统计问题,无修改且允许离线,统计子树信息。

这就是dsu on tree 处理的问题。

算法流程

考虑暴力怎么做,遍历每个子节点的贡献->处理答案->删除子节点贡献。

dsu on tree的实现 :

  1. 遍历所有轻儿子,递归时删除贡献。
  2. 遍历重儿子,保留贡献。
  3. 处理轻儿子的贡献。
  4. 处理当前节点的答案。
  5. 如果该点为轻儿子,删除该点贡献。

为什么不保留多个节点的贡献?

因为会混淆子树之间的信息,由于我们最后处理重儿子的答案,所以递归之后当前子树需要用到他的重儿子贡献,如果都保存的话,遍历下一个子树时会将当前子树的贡献也算进去,所以只能保留一个子树的答案,因为重儿子的子树大小最大,所以保存下之后递归回去需要处理的子节点数更少,从而达到降低复杂度的目的。

主体框架

void dfs1(int u) { // 重剖siz[u] = 1;for (int i = head[u]; i; i = e[i].next) {int v = e[i].v; dfs1(v); siz[u] += siz[v]; if (siz[son[u]] < siz[v]) son[u] = v;}
}
void dfs2(int u, int fa, int opt) { // opt=1表示保留贡献,否则表示删除for (int i = head[u]; i; i = e[i].next) {int v = e[i].v; if (v == son[u] || v == fa) continue ;dfs2(v, u, 0);//处理轻儿子答案}if (son[u]) dfs2(son[u], u, 1), Son = son[u];//处理重儿子答案并保存calc(u);//统计轻儿子贡献/*
处理当前节点答案*/ Son = 0;if (!opt) del(u); // 如果该点轻儿子,删除贡献
//注意:轻儿子的重子节点也要删
}

这里不懂没关系,结合例题再去理解。

例题

例题1

CF600E Lomsat gelral

题目描述

给你一棵以结点 \(1\) 为根的有根树,每个节点最开始都被涂上了颜色。

如果颜色 \(c\) 在以结点 \(v\) 为根的子树中出现次数最多,则称其在以结点 \(v\) 为根的子树中占重要地位。一棵树中可以有很多颜色同时占重要地位

\(v\) 为根的子树指结点 \(v\) 及其他到根结点的路径包含 \(v\) 的结点。

请输出对于每一个结点 \(v\),在其子树中占重要地位的颜色编号之和。

板子,没啥好说的。

细节见代码 code

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N = 1e5 + 5;
int n, c[N], siz[N], son[N], mx, sum, cnt[N], Son, ans[N];
vector<int> g[N];
void dfs1(int u, int fa) {siz[u] = 1;for (auto v : g[u]) {if (v == fa) continue ;dfs1(v, u);siz[u] += siz[v];if (siz[son[u]] < siz[v]) son[u] = v;}
}
inline void add(int u, int fa, int val) {cnt[c[u]] += val;if (cnt[c[u]] > mx) mx = cnt[c[u]], sum = c[u];else if (cnt[c[u]] == mx) sum += c[u];for (auto v : g[u]) {if (v == fa || v == Son) continue ;add(v, u, val);}
}
void dfs2(int u, int fa, int opt) {for (auto v : g[u]) {if (v == fa || v == son[u]) continue ;dfs2(v, u, 0);}if (son[u]) dfs2(son[u], u, 1), Son = son[u];add(u, fa, 1), Son = 0;ans[u] = sum;if (!opt) add(u, fa, -1), sum = 0, mx = 0;
}
signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> n;for (int i = 1; i <= n; i++) cin >> c[i];for (int i = 1, u, v; i < n; i++) {cin >> u >> v;g[u].push_back(v), g[v].push_back(u);}dfs1(1, 0), dfs2(1, 0, 0);for (int i = 1; i <= n; i++) cout << ans[i] << " ";return 0;
}

例题2

P9233 [蓝桥杯 2023 省 A] 颜色平衡树

题目描述

给定一棵树,结点由 \(1\)\(n\) 编号,其中结点 \(1\) 是树根。树的每个点有一个颜色 \(C_i\)

如果一棵树中存在的每种颜色的结点个数都相同,则我们称它是一棵颜色平衡树。

求出这棵树中有多少个子树是颜色平衡树。

也是模板,用一个桶维护每种颜色出现了多少次,再维护一下出现的颜色数量,如果 \(cnt_c \times tot == siz_u\),答案加 \(1\)

其中 \(cnt_c\) 为颜色 \(c\) 的出现次数,\(tot\) 为颜色数,\(siz_u\) 为当前节点 \(u\) 的子树大小。

code

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
#define rd read()
using namespace std;
const int N = 2e5 + 5;
struct Edge {int v, next;
} e[N];
inline int read() {int x = 0; char ch = getchar();while (ch < '0' || ch > '9') ch = getchar();while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();return x;
}
int ans, c[N], n, head[N], cnt, siz[N], son[N], Son, col[N], tot;
inline void add(int u, int v) {e[++cnt] = {v, head[u]}; head[u] = cnt;}
void dfs1(int u) {siz[u] = 1;for (int i = head[u]; i; i = e[i].next) {int v = e[i].v; dfs1(v); siz[u] += siz[v]; if (siz[son[u]] < siz[v]) son[u] = v;}
}
inline void calc(int u, int val) {col[c[u]] += val;if (col[c[u]] == 1 && val == 1) tot++; if (col[c[u]] == 0 && val == -1) tot--;for (int i = head[u]; i; i = e[i].next) {int v = e[i].v; if (v == Son) continue ;calc(v, val);}
}
void dfs2(int u, int opt) {for (int i = head[u]; i; i = e[i].next) {int v = e[i].v; if (v == son[u]) continue ;dfs2(v, 0);}if (son[u]) dfs2(son[u], 1), Son = son[u];calc(u, 1);if (tot * col[c[u]] == siz[u]) ans++;Son = 0;if (!opt) calc(u, -1), tot = 0;
}
signed main() {n = rd; for (int i = 1; i <= n; i++) c[i] = rd, add(rd, i);dfs1(1); dfs2(1, 0);cout << ans;return 0;
}

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

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

相关文章

天河手机网站建设网站建设 讲话

目录 pod启动创建过程 kubelet持续监听的原因 调度概念 调度约束 调度过程 优点 原理 优先级选项 示例 指定调度节点 标签基本操作 获取标签帮助 添加标签&#xff08;Add Labels&#xff09;&#xff1a; 更新标签&#xff08;Update Labels&#xff09; 删除标…

Linux目录下有100百万个文件,如何快速删除

Linux目录下有100百万个文件,如何快速删除Linux目录下有100百万个文件,如何快速删除 利用rsync命令 例:删除/root/files目录下的所有文件ls -l -f /root/files > /tmp/filelist.txt //将目录下的所有文件整理到/…

JavaDay10

Super详解 super注意点: ​ 1.super调用父类的构造方法,必须在构造方法的第一个 ​ 2.super必须只能出现在子类的方法或者构造方法中! ​ 3.super和this不能同时调用构造方法 对比 this: ​ 代表的对象不同: ​ …

29.Linux防火墙管理 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【转】中国信通院《低代码产业发展研究报告(2025年)》核心解读

【转】中国信通院《低代码产业发展研究报告(2025年)》核心解读中国信通院(CAICT)于2025年6月发布的《低代码产业发展研究报告》是中国低代码行业发展的权威性风向标。这份报告不仅全面梳理了低代码市场的最新发展现…

【C++】内存管理 - 指南

【C++】内存管理 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &qu…

昇腾多机推理极速上手:10倍简化的 DeepSeek R1 超大规模模型部署

昇腾多机推理太复杂?易出错?试试 GPUStack在昇腾 NPU 上部署超大规模模型,往往面临一个现实难题:目前主流的官方推理引擎 MindIE 的多机分布式推理虽然性能表现尚可,但配置流程异常复杂。从环境准备、配置初始化到…

python开始exe应用程序初级教程

以下是一个关于如何将Python脚本打包成可执行文件(.exe)的初级教程,使用目前最常用的PyInstaller工具。 准备工作 首先需要安装PyInstaller,打开命令提示符(CMD)或终端,运行以下命令: pip install pyinstaller…

中职校园网站建设建议制作网页的思路

cp -rpf #强行递归复制/etc目录到/mist目录中&#xff0c;并保持源目录的权限等信息不变。 有点类似于打patch&#xff0c;不会改变已有的内容。

凡科可以建设多个网站吗上海手机网站建设电话咨询

用Python解析HTML页面 文章目录 用Python解析HTML页面HTML 页面的结构XPath 解析CSS 选择器解析简单的总结 在前面的课程中&#xff0c;我们讲到了使用 request三方库获取网络资源&#xff0c;还介绍了一些前端的基础知识。接下来&#xff0c;我们继续探索如何解析 HTML 代码&…

网站建设选用平台分析极速蜂app拉新加盟

阿里云服务器是阿里云推出的一种云核算产品&#xff0c;它能够帮助企业和个人快速建立、扩展和管理网络服务。可是&#xff0c;有时候在运用阿里云服务器时&#xff0c;或许会遇到无法装置程序的问题。本文将具体介绍如何处理这个问题。 阿里云服务器无法装置程序或许是由多种原…

版权申请网站宾馆酒店网站建设方案

小伙伴们好久不见&#xff0c;今天我们来聊聊中国 AZURE 的日志分析告警。为什么是中国 AZURE&#xff0c;目前中国 AZURE 的 Monitor 服务和运维相关周围服务和 Global 是有所不同的&#xff0c;所以有些功能和设计不能复制和套用全球版 AZURE 的架构。我们先看一下中国 AZURE…

深入解析:cocos 添加背景,帧动画,贴图

深入解析:cocos 添加背景,帧动画,贴图2025-09-27 10:49 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block…

B站油管抖音一键笔记

在最近,我有一个需求就是需要对视频内容进行一个总结,做成一个笔记,但是又不想自己手动去写,于是我找到了一个项目 BiliNote,BiliNote 是一个开源的 AI 视频笔记助手,支持通过哔哩哔哩、YouTube、抖音等视频链接…

网站安装出现dir更改wordpress地址

集成swagger2的时候swagger-ui.html页面的v2/api-docs接口报404 尝试网上说的权限、包版本不一致、资源路径映射问题&#xff0c;发现都没有问题。 单独访问v2/api-docs接口的时候报 Swagger2Controller Unable to find specification for group 查看相关代码&#xff1a; …

成熟网站开发联系电话陕西网

简介&#xff1a;OpenKruise 是针对 Kubernetes 的增强能力套件&#xff0c;聚焦于云原生应用的部署、升级、运维、稳定性防护等领域。 云原生应用自动化管理套件、CNCF Sandbox 项目 -- OpenKruise&#xff0c;近期发布了 v1.0 大版本。 OpenKruise[1] 是针对 Kubernetes 的…

网站设计公司官网如何设计购物网站

Java流程控制语句有三种&#xff1a; 顺序结构、分支结构和循环结构。 顺序结构&#xff1a; 顺序结构语句是Java程序默认的执行流程&#xff0c;按照代码的先后顺序&#xff0c;从上到下依次执行。 原文链接&#xff1a; Java流程控制控制语句 - 红客网络编程与渗透技术 示例…

介绍自己

大家好!我是一个数据科学与大数据技术专业的大三学生。在日常生活中我有着许多的兴趣爱好打羽毛球、听音乐、做手工。技能树与专业规划 当前技术能力 编程基础:稍微掌握Python数据处理(Pandas、NumPy) 数据库技能:…

pycharm更换国内源

1、找到pip.ini2、记事本修改 [global] timeout = 6000 index-url = https://pypi.tuna.tsinghua.edu.cn/simple trusted-host = https://pypi.tuna.tsinghua.edu.cn

基于Python+Vue开发的反诈视频宣传管理系统源码+运行步骤

项目简介该项目是基于Python+Vue开发的反诈视频宣传管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能,同时锻炼他们的项目设计与开发能力。通过学习…