树上启发式合并(dsu on tree)学习

声明:本文部分内容摘自OI Wiki网站。详情可自行查看学习。

洛谷 P9233

  题目实际上是蓝桥杯 2023 年 A 组省赛的一道题。题干大致的意思是,给定一个含有 n n n 个结点,并且以 1 1 1 为根的一棵树,每个节点 i i i 都有一个颜色 C i C_i Ci,问这棵树有多少个子树是颜色平衡树。其中,如果一棵树中存在的每种颜色的结点个数都相同,则我们称它是一棵颜色平衡树。
  输入:一行一个 n n n,后面的 n n n 行每行一个数对 ( C i , F i ) (C_i,F_i) (Ci,Fi) 分别表示结点 i i i 的颜色和父亲结点。(保证 F 1 = 0 F_1=0 F1=0)。
  输出:这棵树有多少棵子树是颜色平衡树。

题目分析

  感觉题目最大的难点是, C i ≤ 200000 C_i\leq 200000 Ci200000。因为我们最朴素的思想就是:看一棵树是否是颜色平衡树,需要将其子树的颜色情况统计起来。比如,开一个数组c[1..N],其中子树中颜色为i的结点有c[i]个。通过统计一棵树所有子树的c可以得到这棵树的c
  但是现在有一个问题,如果按照上面的思路,显然有 N ≥ C i N\geq C_i NCi;如果每碰到一个子树就开一个c[N],肯定会MLE,并且由于每次合并子树的颜色情况时需要遍历这个数组c,多半也会TLE。
  于是我们又产生了一个想法,用一个map来代替c[N]。因为不是每棵子树都会出现所有的颜色,我们开map不会为没有出现的颜色分配空间,显然空间使用就大大减少了。这种情况下合并子树的颜色情况时,只需要遍历所有子树的map,一一加到树上就行了。
  经过实践,使用map的方法确实不会MLE,但是它TLE了(70 pts)。

  接触到了树上启发式合并这一思想之后,迅速写代码,于是AC。树上启发式合并的核心思想就是保留重儿子。在下面的代码中有注释说明。

AC代码

#include<iostream>using namespace std;int n,c[200005],ccnt[200005],cccnt,sz[200005],ncolor[200005],ecnt=1,fedge[200005],ledge[200005],ans,bigchild[200005];
struct {int end,next;
}edge[200005];void buildarc(int begin,int end){if(!begin)return;if(!fedge[begin])fedge[begin]=ledge[begin]=ecnt;else{edge[ledge[begin]].next=ecnt;ledge[begin]=ecnt;}edge[ecnt++].end=end;
}inline void addcc(int cc){if(!ccnt[cc])cccnt++;ccnt[cc]++;
}
inline void delcc(int cc){ccnt[cc]--;if(!ccnt[cc])cccnt--;
}void add(int node){if(c[ncolor[node]])delcc(c[ncolor[node]]);addcc(c[ncolor[node]]+1);c[ncolor[node]]++;
}
void del(int node){c[ncolor[node]]--;delcc(c[ncolor[node]]+1);if(c[ncolor[node]])addcc(c[ncolor[node]]);
}inline int isbalanced(){return cccnt==1?1:0;}// dfs0 用来求每个子树的大小的,也就是这个子树有多少个结点。
int dfs0(int cur,int father){int childsize,maxchild=0;sz[cur]=1;for(int e=fedge[cur];e;e=edge[e].next){if(edge[e].end==father)continue;sz[cur]+=(childsize=dfs0(edge[e].end,cur));if(childsize>maxchild){maxchild=childsize;bigchild[cur]=edge[e].end;}}return sz[cur];
}// 这个是用来加入/删除某一棵树的,mod == 1 是加入,mod == 0 是删除。
void dfs2(int cur,int father,bool mod){//1 add 0 delif(mod)add(cur);elsedel(cur);for(int e=fedge[cur];e;e=edge[e].next)if(edge[e].end!=father)dfs2(edge[e].end,cur,mod);
}
/***dfs1 是树上启发式合并的核心算法。一个数有多个子树,其中最大的子树的树根成为这个树的重儿子,其它的儿子是轻儿子。树上启发式算法在每个子树上的操作分为 3 步:1.遍历所有轻儿子子树,查看情况,不保留轻儿子子树对原树 c[N] 数组的影响。2.查看重儿子子树的情况,并且保留该子树对原树 c[N] 数组的影响。3.重新加入所有轻儿子子树,从而得到原树的情况。@param cur 当前结点
@param father 当前结点的父节点,用 father = 0 表示没有父节点
@param remain 是否要保留 cur 为根的子树对其父树的影响。也即 cur 是否是重儿子。
***/
void dfs1(int cur,int father,bool remain){//遍历所有轻儿子for(int e=fedge[cur];e;e=edge[e].next)if(edge[e].end!=father && edge[e].end!= bigchild[cur])dfs1(edge[e].end,cur,false);//查看重儿子if(bigchild[cur])dfs1(bigchild[cur],cur,true);//加入根节点add(cur);//重新加入所有轻儿子子树for(int e=fedge[cur];e;e=edge[e].next)if(edge[e].end!=father && edge[e].end!= bigchild[cur])dfs2(edge[e].end,cur,true);ans+=isbalanced();//如果要求不保留影响,cur 为根的树的所有贡献if(!remain)dfs2(cur,father,false);
}int main(){int f;cin>>n;for(int i=1;i<=n;i++){scanf("%d%d",&ncolor[i],&f);buildarc(f,i);}return dfs0(1,0),dfs1(1,0,true),cout<<ans,0;
} 

  AC代码中保留了c[N]数组,其含义与上文所述相同,即颜色的出现次数。可以看到代码中还出现了ccnt数组和cccnt变量。这是一个比较精妙的处理方式,可以在 O ( 1 ) O(1) O(1) 的时间复杂度内判断一棵树是否是颜色平衡树。ccnt[i] = j表示当前共有j个颜色的c值是i,即颜色出现次数的出现次数cccnt颜色出现次数的出现次数的出现次数,当且仅当cccnt == 1的时候,该树是一颗颜色平衡树。
  上面的描述可能有些烧脑,可以用题目给的样例来说明。样例输入:

6
2 0
2 1
1 2
3 3
3 4
1 4

  读者可以根据输入的含义自行画图。对于根节点1而言,颜色1,2,3分别出现了2,2,2次,所以c[1,2,3] = {2,2,2}。根据定义,有3个颜色的出现次数是2,所以ccnt[2] = 3。而对于其它的i != 2,都有cccnt[i] = 0,所以cccnt = 1,因此该树是颜色平衡树。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/307920.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springcloud第4季 springcloud-alibaba之nacos篇

一 nacos 1.1 nacos作用介绍 nacos是一个分布式的配置中心和注册发现中心。 nacos是 dynamic naming configuration service nacosconfigbus 实现动态刷新&#xff1b;nacosconsul 1.2 各个注册中心对比 注册中心CAP模型控制台管理社区活跃度EureakaAp支持低zkcp不支持中…

将Visio绘图导出PDF文件,使其自适应大小,并去掉导入Latex的边框显示

问题描述 将Visio绘图导成pdf文件&#xff0c;首先在Visio绘图如下&#xff1a; 如果直接导出或者另存为pdf文件&#xff0c;则会发现pdf文件是整个页面大小&#xff0c;而不是图片大小。而且在导入latex等排版工具现实时&#xff0c;会显示边框。 问题解决 1.调整Visio中的页…

【攻防世界】题目名称-文件包含

看到 include()&#xff0c;想到文件包含&#xff0c;用php伪协议。 知识点 看到 include()&#xff0c;require()&#xff0c;include_once()&#xff0c;require_once() &#xff0c;想到文件包含&#xff0c;用php伪协议 ?filenamephp://filter/readconvert.base64-encode/…

zabbix监控配置(添加主机、主机组和添加监控项等)

zabbix监控配置 文章目录 zabbix监控配置1.添加主机组2.添加主机&#xff08;linux&#xff09;3.添加主机&#xff08;windows&#xff09;4.监控项配置&#xff08;通过模板添加&#xff09;5.监控项配置&#xff08;手动添加&#xff09; 1.添加主机组 2.添加主机&#xff0…

【C语言】字符串函数和内存函数及其模拟实现

文章目录 前言 一、常见字符串库函数1.strlen函数2.长度不受限制的字符串函数2.1 strcpy2.2 strcat2.3 strcmp 3.长度受限制的字符串函数3.1 strncpy3.2 strncat3.3 strncmp 二、字符串查找函数strstrstrtok 三、strerror函数四、内存操作函数1.memcpy2.memmove3.memcmp 五、字…

水经微图IOS版5.2.0发布

随时随地&#xff0c;微图一下&#xff01; 水经微图&#xff08;简称“微图”&#xff09;IOS新版已上线。 在该版本中主要新增图层树节点排序功能、常规&#xff08;矩形、圆、椭圆、扇形&#xff09;绘制功能、地形夸张等主要功能。 当前版本 当前版本号为&#xff1a;5…

蓝桥杯第2152题——红绿灯

问题描述 爱丽丝要开车去上班, 上班的路上有许多红绿灯, 这让爱丽丝很难过。为 了上班不迟到, 她给自己的车安装了氮气喷射装置。现在她想知道自己上班最 短需要多少时间。 爱丽丝的车最高速度是 米每秒, 并且经过改装后, 可以瞬间加速到小于 等于最高速的任意速度, 也可以瞵…

js可视化爬取数据生成当前热点词汇图

功能 可以爬取到很多数据&#xff0c;并且生成当前的热点词汇图&#xff0c;词越大越热门&#xff08;词云图&#xff09; 这里以b站某个评论区的数据为例&#xff0c;爬取63448条数据生成这样的图片 让我们能够更加直观的看到当前的热点 git地址 可以直接使用&#xff0c;中文…

【C++】类和对象②(类的默认成员函数:构造函数 | 析构函数)

&#x1f525;个人主页&#xff1a;Forcible Bug Maker &#x1f525;专栏&#xff1a;C 目录 前言 类的6个默认成员函数 构造函数 概念 构造函数的特性及用法 析构函数 概念 析构函数的特性及用法 结语 前言 本篇主要内容&#xff1a;类的6个默认成员函数中的构造函…

mysql四种引擎区别

MySQL 提供了多种不同的数据库引擎&#xff0c;其中最常见的有 MyISAM、InnoDB、MEMORY 和 BLACKHOLE。这四个引擎分别有以下特点&#xff1a; 1. MyISAM MyISAM 是 MySQL 的默认引擎。它对于只有较少的修改、大量读取的应用场景具有良好的性能。它不支持事务处理&#xff0c;也…

算法第四十一天-排除排序链表中的重复元素Ⅱ

排除排序链表中的重复元素Ⅱ 题目要求 解题思路 题意&#xff1a;在一个有序链表中&#xff0c;如果一个节点的值出现不止一次&#xff0c;那么把这个节点删除掉 重点&#xff1a;有序链表&#xff0c;所以&#xff0c;一个节点的值出现不止一次&#xff0c;那么他们必相邻。…

LeetCode-416. 分割等和子集【数组 动态规划】

LeetCode-416. 分割等和子集【数组 动态规划】 题目描述&#xff1a;解题思路一&#xff1a;01背包问题&#xff0c;动规五部曲解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分…

【UE 委托】如何利用函数指针理解委托的基本原理

目录 0 引言1 函数指针模拟多播委托 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;UE虚幻引擎专栏&#x1f4a5; 标题&#xff1a;【UE 委托】如何利用函数指针理解委托的基本原理❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难…

OpenCV C++学习笔记

1.图像的读取与显示 1.1 加载并显示一张图片 #include<opencv2/opencv.hpp> #include<iostream>using namespace cv; using namespace std; int main(int argc,char** argv){Mat srcimread("sonar.jpg");//读取图像if(src.empty()){printf("Could…

ORA-00600: internal error code, arguments: [krbcbp_9]

解决方案 1、清理过期 2、control_file_record_keep_time 修改 恢复时间窗口 RMAN (Recovery Manager) 是 Oracle 数据库的备份和恢复工具。在 RMAN 中&#xff0c;可以使用“恢复窗口”的概念来指定数据库可以恢复到的时间点。这个时间点是基于最近的完整备份或增量备份。 …

创建一个qt登录界面,密码账号正确转到窗口2,否则弹出对话框提示账号密码错误,窗口2有四个按键,三个按键可以朗读按键文本,第四个退出。

作业要求&#xff1a; 主函数&#xff1a; int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();Form1 f;//连接窗口1的信号函数和窗口2打开的lambda函数Widget::connect(&w,&Widget::login,[&](){f.show();});return a.exec(); }窗…

leetcode73 矩阵置零

题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用原地算法。 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 输入&#xff1a;matrix [[0,1,2,0],[3,4…

力扣 |142. 环形链表 II

用快慢指针的方法 根据推出的表达式&#xff1a;slow和fast相遇的时候&#xff0c;让slow和位于头节点的p同时 向前走&#xff0c;刚好在入环的节点处相遇&#xff01;注意&#xff1a;b和c交界的点不一定是从例如-4这个节点处&#xff0c; 可能是0节点处。因为相遇的点只能是…

pycharm2024关闭项目后一直显示正在关闭项目

网上的很多教程都试了不行&#xff0c;直接用下面的方法有效解决。 点击 帮助--查找操作--输入Registry--点注册表&#xff0c;取消ide.await.scope.completion后的勾选即可。

(Oracle)SQL优化案例:隐式转换优化

项目场景 项目现场的某个kettle模型执行非常缓慢&#xff0c;原因在于某个SQL执行效率非常的低。甲方得知此事要求公司赶紧优化&#xff0c;负责该模块的同事对SQL优化并不熟悉。所以作为一个立志成为优秀DBA的ETL工程师&#xff0c;我自告奋勇&#xff1a;不是DBA&#xff0c;…