树上启发式合并属于暴力的优化,复杂度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)的更多相关文章

  1. 神奇的树上启发式合并 (dsu on tree)

    参考资料 https://www.cnblogs.com/zhoushuyu/p/9069164.html https://www.cnblogs.com/candy99/p/dsuontree.ht ...

  2. 树上启发式合并 (dsu on tree)

    这个故事告诉我们,在做一个辣鸡出题人的比赛之前,最好先看看他发明了什么新姿势= =居然直接出了道裸题 参考链接: http://codeforces.com/blog/entry/44351(原文) ...

  3. 【CF600E】Lomset gelral 题解(树上启发式合并)

    题目链接 题目大意:给出一颗含有$n$个结点的树,每个节点有一个颜色.求树中每个子树最多的颜色的编号和. ------------------------- 树上启发式合并(dsu on tree). ...

  4. dsu on tree 树上启发式合并 学习笔记

    近几天跟着dreagonm大佬学习了\(dsu\ on\ tree\),来总结一下: \(dsu\ on\ tree\),也就是树上启发式合并,是用来处理一类离线的树上询问问题(比如子树内的颜色种数) ...

  5. 树上启发式合并(dsu on tree)学习笔记

    有丶难,学到自闭 参考的文章: zcysky:[学习笔记]dsu on tree Arpa:[Tutorial] Sack (dsu on tree) 先康一康模板题吧:CF 600E($Lomsat ...

  6. dsu on tree (树上启发式合并) 详解

    一直都没出过算法详解,昨天心血来潮想写一篇,于是 dsu on tree 它来了 1.前置技能 1.链式前向星(vector 建图) 2.dfs 建树 3.剖分轻重链,轻重儿子 重儿子 一个结点的所有 ...

  7. 【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)

    (这题在洛谷主站居然搜不到--还是在百度上偶然看到的) 题目描述 给一棵根为1的树,每次询问子树颜色种类数 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数 接下来n-1行,每行一条边 接下 ...

  8. CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths 树上启发式合并(DSU ON TREE)

    题目描述 一棵根为\(1\) 的树,每条边上有一个字符(\(a-v\)共\(22\)种). 一条简单路径被称为\(Dokhtar-kosh\)当且仅当路径上的字符经过重新排序后可以变成一个回文串. 求 ...

  9. hdu6191(树上启发式合并)

    hdu6191 题意 给你一棵带点权的树,每次查询 \(u\) 和 \(x\) ,求以 \(u\) 为根结点的子树上的结点与 \(x\) 异或后最大的结果. 分析 看到子树,直接上树上启发式合并,看到 ...

随机推荐

  1. Javascript实现base64的加密解密方法

    1 function Base64() { 2 // private property 3 _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkl ...

  2. 03 最小CMake项目

    03 最小CMake项目 所有CMake项目都从一个CMakeLists.txt文件开始,此文件应该放在源代码树的最顶层目录下.可以将CMakeLists.txt想象成CMake项目文件,定义了从源和 ...

  3. 4、StringBuilder类

    StringBuilder类 一个可变的字符序列,此类提供一个与StringBuffer 兼容的 API,但不保证同步(StringBuilder 不是线程安全). 该类被设计用作 StringBuf ...

  4. 【项目实战】自备相机+IMU跑通Vins-Mono记录

    前言 初次接触SLAM,公司要求用自己的设备来跑通vinsmono这个程序,虽然已经跑通了别人的数据包,但是真正自己上手来运行这个程序,发现真的是困难重重,特意在此记载下来整个过程,以供大家参考. 我 ...

  5. Nginx 动态压缩与静态压缩,显著提高前后端分离项目响应速度!

    文章转载自:https://mp.weixin.qq.com/s/NuTmEUQU5L69is53bCauKA Nginx 中配置前端的 gzip 压缩,有两种思路: Nginx 动态压缩,静态文件还 ...

  6. kubectl top命令

    kubectl top命令可显⽰节点和Pod对象的资源使⽤信息,它依赖于集群中的资源指标API来收集各项指标数据.它包含有node和pod两个⼦命令,可分别⽤于显⽰Node对象和Pod对象的相关资源占 ...

  7. Compose 模板文件

    模板文件是使用 Compose 的核心,涉及到的指令关键字也比较多.但大家不用担心,这里面大部分指令跟 docker run 相关参数的含义都是类似的. 默认的模板文件名称为 docker-compo ...

  8. PR中导入的照片在画面上只显示一部分的解决办法

    这是因为图片的分辨率太大导致的. 1.图片的分辨率大于PR工程的设置的时候,就会只显示局部. 2.可以用PS把图片先缩小为工程大小即可. 3.也可以把图片拖到时间线上,右键点击,缩放为帧大小即可. 这 ...

  9. EasyExcel实现文件导出

    官网:https://www.yuque.com/easyexcel/doc/easyexcel 导出 准备工作 引入依赖 <!--EasyExcel相关依赖--> <depende ...

  10. PAT (Basic Level) Practice 1020 月饼 分数 25

    月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼.现给定所有种类月饼的库存量.总售价.以及市场的最大需求量,请你计算可以获得的最大收益是多少. 注意:销售时允许取出一部分库存.样 ...