树上启发式合并属于暴力的优化,复杂度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. 云服务器上搭建cobalt strike遇到的一些小问题

    一.前言: 当你兴高采烈的买了一台云服务器,迫不及待地想去搭建传说中的神器cobalt strike的时候,你可能会遇到以下的一些小问题,这里我会列出对应的解决方法. 二.遇到的一些小问题 1.上传文 ...

  2. matery添加加载动画

    1.在主题 /layout/_partial/目录新建一个loading-pages.ejs 内容如下: <style type="text/css" lang=" ...

  3. windows绕过杀软添加账户密码

    windows绕过杀软添加账户密码 起因:system权限下存在杀软无法添加账户信息 绕过方法 1.C#脚本 运行后会在目标机器上创建一个用户为 wh4am1 密码为 qqai@love 的 Admi ...

  4. 【c语言学习】1 基础环境安装调试

    1-1下载 vs2019 vs2019下载链接https://visualstudio.microsoft.com/zh-hans/vs/community/ 1-2安装配置环境 记得勾选上c++开发 ...

  5. 08_Linux基础-vim-tmux-字符编码

    @ 目录 08_Linux基础-vim-tmux-字符编码 一. vim vim编辑器作用 vim模式 vim命令模式 vim编辑模式 vim末行模式 vim视图模式 vim替换模式 练习 vim常用 ...

  6. maven-scope属性

    Maven 中的 scope 属性解释 <dependency> <groupId>org.glassfish.web</groupId> <artifact ...

  7. Svelte Ui Admin后台管理系统|svelte3+svelteUI中后台前端解决方案

    基于svelte3.x+svelteKit+svelte-ui网页后台管理系统SvelteAdmin. Svelte-Ui-Admin 基于svelte3.x+svelteKit+vite3+echa ...

  8. Security Context

    概述 Security Context(安全上下文)用来限制容器对宿主节点的可访问范围,以避免容器非法操作宿主节点的系统级别的内容,使得节点的系统或者节点上其他容器组受到影响. Security Co ...

  9. 记一次TIME_WAIT网络故障

    文章转载自:https://blog.51cto.com/dngood/988968

  10. Git 便捷操作

    虽然现在有很多图形化的 Git 工具,但是命令行依然 yyds.本文记录了工作中很有用的一些 Git 操作. 1.Fork出来的Git仓库同步代码 背景:有的时候从原仓库fork出了一个新仓库,这个新 ...