[算法学习] dsu on tree
简介
dsu on tree跟dsu没有关系,但是dsu on tree借鉴了dsu的启发式合并的思想。
它是用来解决一类树上的询问问题,一般这种问题有以下特征:
\(1.\)只有对子树的查询;
\(2.\)没有修改。
如果满足以上特征,那么dsu on tree很可能就可以派上用场了。
算法
我们以CF600E Lomsat gelral为例。
Descrption
一棵以\(1\)为根的树有 \(n\) 个结点,每个结点的颜色是\(c_i\),每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。
数据范围\(1\le n\le 10^5, 1\le c_i\le 10^5\)。
Solution
我们考虑暴力怎么写:遍历每一个节点 \(u\) ,然后把子树内的所有颜色暴力统计出来更新答案。
然后消除节点 \(u\) 的贡献,继续递归计算其他点的贡献。
复杂度\(O(n^2)\),显然是很不优美的。
然后dsu on tree就登场了。它也是一个暴力,但是它结合了轻重链剖分,将复杂度降到了\(O(nlogn)\)。
我们先跑一个\(dfs1\),记录节点\(u\)的重儿子\(heavy[u]\)。
接下来:
- 遍历每一个节点。
- 递归所有的轻儿子,递归进入时暴力加贡献,递归结束时暴力消除贡献。
- 递归重儿子,递归进入时暴力加贡献,递归结束时不消除贡献。
- 统计所有轻儿子的答案,并更新该节点的答案。
- 删除所有轻儿子对答案的影响。
复杂度\(O(nlogn)\),可以通过本题。
Code
// Author: wlzhouzhuan
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
inline int read() {
int x = 0, neg = 1; char op = getchar();
while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
return neg * x;
}
inline void print(int x) {
if (x < 0) { putchar('-'); x = -x; }
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 100005;
int n;
vector <int> adj[N];
void add(int u, int v) { adj[u].pb(v); }
int sz[N], fa[N], heavy[N]; // heavy[u] 表示u的重儿子
void dfs1(int u, int f) {
sz[u] = 1, fa[u] = f;
for (auto v: adj[u]) {
if (v == f) continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[heavy[u]]) {
heavy[u] = v; // 更新重儿子
}
}
}
long long ans[N], sum;
int cnt[N], col[N], Son, Max;
void change(int u, int val) {
cnt[col[u]] += val;
if (cnt[col[u]] > Max) Max = cnt[col[u]], sum = col[u];
else if (cnt[col[u]] == Max) sum += col[u];
for (auto v: adj[u]) {
// 由于重儿子的信息没有被删去,所以已经统计过了,不能再计算
if (v == fa[u] || v == Son) continue;
change(v, val);
}
}
void dfs2(int u, int keep) {
for (auto v: adj[u]) {
if (v == fa[u] || v == heavy[u]) continue;
dfs2(v, 0); // 遍历u的轻儿子
}
if (heavy[u]) dfs2(heavy[u], 1), Son = heavy[u];
change(u, 1), ans[u] = sum, Son = 0;
if (!keep) change(u, -1), sum = Max = 0;
}
int main() {
n = read();
for (rint i = 1; i <= n; i++) {
col[i] = read();
}
for (rint i = 1; i < n; i++) {
int x = read(), y = read();
add(x, y), add(y, x);
}
int root = 1; // 题目默认1为根
dfs1(root, 0);
dfs2(root, 0);
for (rint i = 1; i <= n; i++) {
printf("%lld ", ans[i]);
}
return 0;
}
[算法学习] dsu on tree的更多相关文章
- 【算法】dsu on tree初探
dsu on tree的本质是树上的启发式合并,它利用启发式合并的思想,可以将O(N^2)的暴力优化成O(NlogN),用于不带修改的子树信息查询. 具体如何实现呢?对于一个节点,继承它重儿子的信息, ...
- Note -「Dsu On Tree」学习笔记
前置芝士 树连剖分及其思想,以及优化时间复杂度的原理. 讲个笑话这个东西其实和 Dsu(并查集)没什么关系. 算法本身 Dsu On Tree,一下简称 DOT,常用于解决子树间的信息合并问题. 其实 ...
- dsu on tree详解
这个算法还是挺人性化的,没有什么难度 就是可能看起来有点晕什么的. 大体 思想是 利用重链刨分来优化子树内部的查询. 考虑一个问题要对每个子树都要询问一次.我们暴力显然是\(n^2\)的. 考虑一下优 ...
- dsu on tree入门
先瞎扯几句 说起来我跟这个算法好像还有很深的渊源呢qwq.当时在学业水平考试的考场上,题目都做完了不会做,于是开始xjb出题.突然我想到这么一个题 看起来好像很可做的样子,然而直到考试完我都只想出来一 ...
- 【CodeForces】741 D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)
[题意]给定n个点的树,每条边有一个小写字母a~v,求每棵子树内的最长回文路径,回文路径定义为路径上所有字母存在一种排列为回文串.n<=5*10^5. [算法]dsu on tree [题解]这 ...
- 【CodeForces】600 E. Lomsat gelral (dsu on tree)
[题目]E. Lomsat gelral [题意]给定n个点的树,1为根,每个点有一种颜色ci,一种颜色占领一棵子树当且仅当子树内没有颜色的出现次数超过它,求n个答案——每棵子树的占领颜色的编号和Σc ...
- dsu on tree (树上启发式合并) 详解
一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...
- [dsu on tree]【学习笔记】
十几天前看到zyf2000发过关于这个的题目的Blog, 今天终于去学习了一下 Codeforces原文链接 dsu on tree 简介 我也不清楚dsu是什么的英文缩写... 就像是树上的启发式合 ...
- [学习笔记]Dsu On Tree
[dsu on tree][学习笔记] - Candy? - 博客园 题单: 也称:树上启发式合并 可以解决绝大部分不带修改的离线询问的子树查询问题 流程: 1.重链剖分找重儿子 2.sol:全局用桶 ...
随机推荐
- 【Android开发】EasyPermissions 请求权限
安卓6.0以后,开发应用的时候,仅在AndroidManifest.xml中申请权限已经不可以了,需要在代码中动态申请. 现在看一个google推出的机制:EasyPermissions 引入步骤: ...
- 安卓xml布局中 android:paddingBottom="@dimen/activity_vertical_margin"是什么意思?
@dimen/activity_vertical_margin 是什么意思? @dimen/activity_vertical_margin这个的意思就是在你的values文件夹下面的dimens文件 ...
- c++对c的拓展_引用的基本用法
实质:取别名 格式:原类型&别名=原变量名: 注意:1.定义时必须初始化 2.初始化后不能够改变指向 3.不可对Null进行引用 4.可对任意类型取别名包括数组(int (&别名)[个 ...
- CentOS7.x 离线安装和开机启动 supervisor 4.2.4
CentOS7.x 服务器 离线安装 开机启动 supervisor 4.2.4
- 4.Java开发环境的搭建
Java开发环境搭建 一.JDK下载与安装 JDK8下载地址 选择目录,点击下一步 二.配置环境变量 变量名:JAVA_HOME 变量值:JDK安装路径 变量值:CLASSPATH 变量值:.;%JA ...
- VMware下ubuntu 20.04扩容/磁盘
最近搞zabbix监控,发现搭建的监控server主机磁盘告警.提示/超过阈值80%. 有实在VMware软件下的虚拟机,首先我就是给虚机磁盘增加容量. 增加后发现没什么改变,看来还需要其他操作. 在 ...
- Fastjsonfan反序列链学习前置知识
Fastjson前置知识 Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象. Fastjson 可以操作任何 ...
- 使用钡铼BL102网关连接西门子S7-1200PLC 以及mosquitto服务器方法
一.软硬件描述 西门子PLC S7-1215 钡铼BL102网关 mosquitto MQTT服务器(腾讯云上搭建) 可以上网的路由器一套 二.需要使用的软件. 西门子Portal v15.1 (西门 ...
- 教你轻松解决CSRF跨站请求伪造攻击
摘要:CSRF(Cross-site request forgery)跨站请求伪造,通过伪装来自受信任用户的请求来利用受信任的网站.与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也 ...
- 如何配置JAVA环境并安装IEAD软件
安装IDEA软件之前需要做哪些准备? 在安装IDEA软件之前,需要先确定电脑中有没有JDK,如果没有需要先安装JDK. JDK是整个JAVA的核心,包括了Java运行环境,Java工具(javac/j ...