树上启发式合并属于暴力的优化,复杂度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. xray+awvs的联动

    前言:xray是一款强大的漏扫工具,配合awvs的爬站功能可以十分轻松实现全自动挖洞,这里awvs我是使用的是12版本 1.启动xray,监听本地指定端口,这里我监听的是2222端口 命令:xray_ ...

  2. screen -中断保留-屏幕同步

    工作中经常用到 screen 用处: 中断保留 和屏幕同步. yum install screen screen -S name 创建 -ls 查看 -r 恢复 -x 同屏

  3. [CF1519D] Maximum Sum of Products (暴力)

    题面 有两个长为 n n n 的序列 a a a 和 b b b,至多反转 a a a 的一个子区间,最大化 ∑ i = 1 n a i ⋅ b i \sum_{i=1}^na_i\cdot b_i ...

  4. 第九十五篇:vue-router的导航守卫

    好家伙,考完期末考了. 恢复博客更新 1.什么是导航守卫? "导航"表示路由正在发生变化 设置导航,就在切换过程中进行限制   "守卫"就好理解了 盯着你,不然 ...

  5. EntityFrameworkCore 模型自动更新(上)

    话题 嗯,距离上一次写博文已经过去近整整十个月,还是有一些思考,但还是变得懒惰了,心思也不再那么专注,有点耗费时间,学习也有点停滞不前,那就顺其自然,随心所欲吧,等哪天心血来潮,想写了就写写 模型自动 ...

  6. python一招完美搞定Chromedriver的自动更新

    日常的web自动化过程中,我们常常用python selenium库来操纵Chrome浏览器实现网页的自动化.这其中有个比较头疼的问题:Chrome的更新频率非常频繁,与之对应的Chromedrive ...

  7. thinkphp5.1 cookie跨域、thinkphp5.1 session跨域、tp5.1cookie跨域

    cookie跨域: //config/cookie.php return [ //... //仅7.3.0及以上适用 'samesite' => 'None', //是否加密cookie值,fa ...

  8. 【项目实战】CNN手写识别

    由于只需要修改之前基于ANN模型代码的模型设计部分所以篇幅较短,简单的加点注释给自己查看即可 视频链接:https://www.bilibili.com/video/BV1Y7411d7Ys?p=10 ...

  9. ES 7.13版本设置索引模板和索引生命周期管理

    第一步:索引管理中查看都有哪些索引文件,然后添加索引模式(后面的日期用*表示) 第二步:索引生命周期管理 自带的有一个log,就使用这个,不用再新建了,根据需求修改里面的配置就行了 第三步:添加索引模 ...

  10. Dockerfile文件:设置变量启动的时候传递进去

    from openjdk:8-jdk-alpine RUN ln -sf /usr/share/zoneinfo/Asia/shanghai /etc/localtime RUN echo 'Asia ...