树上启发式合并(dsu on tree)
树上启发式合并属于暴力的优化,复杂度O(nlogn)
主要解决的问题特点在于:
1.对于树上的某些信息进行查询
2.一般问题的解决不包含对树的修改,所有答案可以离线解决
算法思路:这类问题的特点在于父节点的信息是通过子节点更新而来
所以如果是暴力解决的话就是对每一个节点往下跑一次图,复杂度在O(n^2)
因为父节点是有子节点跟新而来,所以我们可以考虑每次保留一部分子节点的信息,将另一部分子节点信息暴力合并得到父节点的信息
这样的话我们就考虑保留重子节点的信息(子树中子节点数最多的节点),这样我们就可以减少合并次数
从树链剖分的性质中我们可以得知,一个点到根节点最多不超过logn条路径,所以这样就将算法的复杂度大大降低为O(nlogn)
引例一:https://www.luogu.com.cn/problem/U41492
求一颗子树中出现的颜色个数
我们的具体做法如下:
1.每次先遍历轻子节点,计算轻子节点的信息和答案,但是不保存其信息
2.遍历重子节点,计算重子节点信息和答案,保存其信息
3.暴力合并轻子节点信息到重子节点上
这里比较有疑惑的就是第一和第三步能不能合并到一起,以及为什么在第一步不保存轻子节点的信息。
其实这是同一个问题,之所以在第一次遍历轻子节点的时候不保存其信息,是为了避免轻子节点的信息对重子节点信息的影响
换句人话讲:因为我们统计节点颜色的时候,我们是定义一个cnt[]数组进行统计,假设我们第一次遍历轻子节点的时候保留信息
cnt[]数组记录下当前有的颜色数为totcol = 5,那么在遍历重子节点的时候,如果遍历到一个新的颜色,totcol这个时候会变成6,进而
在保存数据的时候在重子节点这记录为当前子树内的颜色数为6,这就造成了数据错误,如果不记录轻子节点信息的情况下,当回溯到
父节点时,轻子节点的信息会不断随着del()函数,totcol减少,然后从0开始遍历重子节点,这样才能正确的计算出重子节点重蕴含的信息。
1 # include<iostream>
2 # include<bits/stdc++.h>
3 using namespace std;
4 # define int long long
5 # define endl "\n"
6 const int N = 2e5 + 10;
7 int sz[N], big[N], col[N], l[N], r[N], rnk[N], totdfn;
/*
sz[]子树大小
big[]重儿子
l[],r[]dfs序列下,子树的边界
rnk[] dfs序列对应的节点编号
totdfn dfs序列
*/
8 int ans[N], cnt[N], totcolor;
9 vector<int> g[N];
10 void add(int u) {
11 if (cnt[col[u]] == 0) ++totcolor;
12 cnt[col[u]]++;
13 }
14
15 void del(int u) {
16 cnt[col[u]]--;
17 if (cnt[col[u]] == 0) --totcolor;
18 }
19
20 int getans() {
21 return totcolor;
22 }
23 void dfs0(int u, int fa) {
24 l[u] = ++totdfn;
25 rnk[totdfn] = u;
26 sz[u] = 1;
27 for (int v : g[u]) {
28 if (v != fa) {
29 dfs0(v, u);
30 sz[u] += sz[v];
31 if (!big[u] || sz[v] > sz[big[u]]) big[u] = v;
32 }
33
34 }
35 r[u] = totdfn;
36 }
37 //keep表示是否保留节点信息
38 void dfs1(int u, int fa, bool keep) {
39 for (int v : g[u]) {
40 if (v != fa && v != big[u]) {
41 dfs1(v, u, false);
42 }
43 }//计算轻子节点信息
44 if (big[u]) {
45 dfs1(big[u], u, true);
46 }//计算重子节点信息
47 for (int v : g[u]) {
48 if (v != fa && v != big[u]) {
49 for (int i = l[v]; i <= r[v]; ++i) {
50 add(rnk[i]);
51 }
52 }
53 }
54 add(u);
55 ans[u] = getans();
56 if (keep == false) {
57 for (int i = l[u]; i <= r[u]; ++i) {
58 del(rnk[i]);
59 }
60 }//删除轻子节点信息
61 }
62
63 void solve() {
64 int n;
65 cin >> n;
66 for (int i = 1; i <= n; ++i) g[i].clear();
67 for (int i = 1; i < n; ++i) {
68 int u, v;
69 cin >> u >> v;
70 g[u].push_back(v);
71 g[v].push_back(u);
72 }
73 for (int i = 1; i <= n; ++i) cin >> col[i];
74 dfs0(1, 0);
75 dfs1(1, 0, false);
76 int m;
77 cin >> m;
78 while (m--) {
79 int k;
80 cin >> k;
81 cout << ans[k] << endl;
82 }
83
84 }
85 int tt;
86 signed main() {
87 ios::sync_with_stdio(false);
88 cin.tie(0);
89 cout.tie(0);
90 tt = 1;
91 while (tt--)solve();
92
93
94 return 0;
95 }
树上启发式合并(dsu on tree)的更多相关文章
- 神奇的树上启发式合并 (dsu on tree)
参考资料 https://www.cnblogs.com/zhoushuyu/p/9069164.html https://www.cnblogs.com/candy99/p/dsuontree.ht ...
- 树上启发式合并 (dsu on tree)
这个故事告诉我们,在做一个辣鸡出题人的比赛之前,最好先看看他发明了什么新姿势= =居然直接出了道裸题 参考链接: http://codeforces.com/blog/entry/44351(原文) ...
- 【CF600E】Lomset gelral 题解(树上启发式合并)
题目链接 题目大意:给出一颗含有$n$个结点的树,每个节点有一个颜色.求树中每个子树最多的颜色的编号和. ------------------------- 树上启发式合并(dsu on tree). ...
- dsu on tree 树上启发式合并 学习笔记
近几天跟着dreagonm大佬学习了\(dsu\ on\ tree\),来总结一下: \(dsu\ on\ tree\),也就是树上启发式合并,是用来处理一类离线的树上询问问题(比如子树内的颜色种数) ...
- 树上启发式合并(dsu on tree)学习笔记
有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...
- dsu on tree (树上启发式合并) 详解
一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...
- 【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)
(这题在洛谷主站居然搜不到--还是在百度上偶然看到的) 题目描述 给一棵根为1的树,每次询问子树颜色种类数 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数 接下来n-1行,每行一条边 接下 ...
- CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 树上启发式合并(DSU ON TREE)
题目描述 一棵根为\(1\) 的树,每条边上有一个字符(\(a-v\)共\(22\)种). 一条简单路径被称为\(Dokhtar-kosh\)当且仅当路径上的字符经过重新排序后可以变成一个回文串. 求 ...
- hdu6191(树上启发式合并)
hdu6191 题意 给你一棵带点权的树,每次查询 \(u\) 和 \(x\) ,求以 \(u\) 为根结点的子树上的结点与 \(x\) 异或后最大的结果. 分析 看到子树,直接上树上启发式合并,看到 ...
随机推荐
- ORA-01950: no privileges on tablespace 'USERS'-- 解决办法
ORA-01950: no privileges on tablespace 'USERS' 原因: 在表空间 "USERS" 无权限 解决办法: 用户登录,查看当前用户所 ...
- scratch制作彩虹猫病毒模拟器
scratch制作彩虹猫病毒模拟器 hello,大家好. 编程慢慢更加接近生活,甚至小孩也开始学了,比如scratch编程,小编今天就带了一件作品(彩虹猫病毒模拟器) 我们先看一下效果| 做的还可以, ...
- 内网技巧-通过SAM数据库获得本地用户hash的方法
内网技巧-通过SAM数据库获得本地用户hash的方法 在windows上的C:\Windows\System32\config目录保存着当前用户的密码hash.我们可以使用相关手段获取该hash. 提 ...
- kafka详解(一)--kafka是什么及怎么用
kafka是什么 在回答这个问题之前,我们需要先了解另一个东西--event streaming. 什么是event streaming 我觉得,event streaming 是一个动态的概念,它描 ...
- OSI七层模型与TCP/IP协议
作者:菘蓝 时间:2022/9/1 ================================================================================== ...
- zookeeper_mac安装总结
Zookeeper mac安装总结 1. 执行 brew install zookeeper 可能遇到报错 Error: The following directories are not writa ...
- Mac_linux_ssh防掉线
vi ~/.ssh/config 加入一条 ServerAliveInterval 60 #客户端主动向服务端请求响应的间隔
- 4、StringBuilder类
StringBuilder类 一个可变的字符序列,此类提供一个与StringBuffer 兼容的 API,但不保证同步(StringBuilder 不是线程安全). 该类被设计用作 StringBuf ...
- Dockerfile中ADD命令详细解读
ADD指令的功能是将主机构建环境(上下文)目录中的文件和目录.以及一个URL标记的文件 拷贝到镜像中. 其格式是: ADD 源路径 目标路径 #test FROM ubuntu MAINTAINER ...
- Docker 数据共享与持久化