当前位置: 首页 > news >正文

DSU ON TREE

DSU ON TREE

DSU:并查集
DSU ON TREE:树上启发式合并
我也不知道为啥树上并查集就是树上启发式合并

启发式合并的思想是每次把小的往大的合并,也就是最大化利用已有的答案(大的数组不用清空,在原基础上加上小的即可)。转移到树上,“大”显然就是树的重心

能解决什么样的问题?需要统计子树信息,但是子树的信息不好合并。比如权值是否出现(桶)。所以肯定要留下最大的,也就是树链剖分的重儿子。

考虑两种合并方式(以对子树做桶排序为例,保留重儿子数组):

  • 遍历子树的桶,对应相加,即类似num[x][val]+=num[v][val],复杂度O(值域)
  • 遍历子树,直接num[x][val[v]]++,复杂度O(子树大小)

显然第一种太大了。

同时,显然不能对每个节点开一个桶表示“以x为根的子树的桶”,空间无法接受,所以桶只能留到一维,这就涉及到清空,因为在dfs另一个儿子时前一个子树的影响要清空。所以要尽可能少的减少清空,在dfs时如果最后访问重儿子,那就可以不清空最大的一部分。

void dfs2(int x,int fa,int save)
{for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa || v==mxson[x]) continue;dfs2(v,x,0);}if (mxson[x]) dfs2(mxson[x],x,1);if (!show[dis[x]]) show[dis[x]]=deep[x];else show[dis[x]]=min(show[dis[x]],deep[x]);int need=k+2*dis[x]-dis[x];if (show.count(need)){int mndep=show[need];int nowans=mndep+deep[x]-2*deep[x];	ans=min(ans,nowans);}for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa || v==mxson[x]) continue;calc_ans(v,x,x),add(v,x);}//if (!save) del(x,fa);if (!save) show.clear();
}

大致这个样子,save表示是否要清空桶。先跑轻儿子,清空桶。再跑重儿子,不清空桶,那么这个桶里的东西再回溯到父亲节点时依然保留。同时注意为什么先calc_ans再add,是为了避免有两个点在同一棵子树内的情况,即 u → l c a → x → l c a → v u\rightarrow lca\rightarrow x\rightarrow lca\rightarrow v ulcaxlcav的情况。在题目里往往这种情况不合法。

例题

洛谷P4149 [IOI2011] Race
题目链接
题目大意:给一棵树,每条边有权。求一条简单路径,权值和等于k,且边的数量最小。
思路:问题转化为选择点对 ( u , v ) (u,v) (u,v),满足 d i s u + d i s v − d i s l c a ( u , v ) = k dis_u+dis_v-dis_{lca(u,v)}=k disu+disvdislca(u,v)=k,最小化 d e e p u + d e e p v − d e e p l c a ( u , v ) deep_u+deep_v-deep_{lca(u,v)} deepu+deepvdeeplca(u,v),考虑处理以 x x x为根的子树的答案,不妨设 l c a ( u , v ) = x lca(u,v)=x lca(u,v)=x,在dfs到点u时,只需要查找 k + 2 ∗ d i s x − d i s u k+2*dis_x-dis_u k+2disxdisu的点,都可以作为点 v v v(移项可得),此时考虑需要最小化的值, d e e p u deep_u deepu d e e p x deep_x deepx都是已知值,所以只需要开一个桶(map)维护 m a p [ d ] map[d] map[d]表示 d i s = d dis=d dis=d的点的 d e e p deep deep最小值。
解决了思路,剩下的就是实现DSU ON TREE。注意先遍历子树求解,再将该子树加入桶。

#include<bits/stdc++.h>
#define pa pair<int,int>
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_backusing namespace std;inline ll read()
{ll f=1,sum=0;char c=getchar();while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}return sum*f;
}
const int MAXN=200010;
struct edge{int next,to,val;
}e[MAXN*2];
int head[MAXN],cnt;
void addedge(int u,int v,int w)
{e[++cnt].next=head[u];e[cnt].to=v;e[cnt].val=w;head[u]=cnt;
}
int sz[MAXN],mxson[MAXN],mxsz[MAXN];
int deep[MAXN],ans,k;
ll dis[MAXN];
void dfs1(int x,int fa)
{sz[x]=1;for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa) continue;dis[v]=dis[x]+e[i].val;deep[v]=deep[x]+1;dfs1(v,x);sz[x]+=sz[v];if (sz[v]>mxsz[x]) mxson[x]=v,mxsz[x]=sz[v];}
}
map <ll,int> show;
void del(int x,int fa)
{show[dis[x]]=0;for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa) continue;del(v,x);}
}
void add(int x,int fa)
{if (!show.count(dis[x])) show[dis[x]]=deep[x];else show[dis[x]]=min(show[dis[x]],deep[x]);for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa) continue;add(v,x);}
}
void calc_ans(int x,int fa,int rt)
{int need=k+2*dis[rt]-dis[x];if (show.count(need)){int mndep=show[need];int nowans=mndep+deep[x]-2*deep[rt];ans=min(ans,nowans);}for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa) continue;calc_ans(v,x,rt);}
}
void dfs2(int x,int fa,int save)
{for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa || v==mxson[x]) continue;dfs2(v,x,0);}if (mxson[x]) dfs2(mxson[x],x,1);if (!show[dis[x]]) show[dis[x]]=deep[x];else show[dis[x]]=min(show[dis[x]],deep[x]);int need=k+2*dis[x]-dis[x];if (show.count(need)){int mndep=show[need];int nowans=mndep+deep[x]-2*deep[x];	ans=min(ans,nowans);}for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa || v==mxson[x]) continue;calc_ans(v,x,x),add(v,x);}//if (!save) del(x,fa);if (!save) show.clear();
}
int main()
{int n=read(); k=read();for (int i=1;i<n;i++){int u=read()+1,v=read()+1,w=read();addedge(u,v,w);addedge(v,u,w);}deep[1]=1;dfs1(1,0);//for (int i=1;i<=n;i++) cout<<deep[i]<<' '<<dis[i]<<' '<<mxson[i]<<endl;ans=INF;dfs2(1,0,0);if (ans==INF) cout<<-1<<endl;else cout<<ans<<endl;return 0;
}

有一个小细节是要单独计算一下根的答案,因为在后面的过程中并没有再次进入重儿子,所以会漏掉重儿子到子树树根的这种答案。其他情况都已经在后面的不断加入中包含。

例题

CF 600E Lomsat gelral
题目链接
题目大意:一棵树每个点有个颜色,求以每个点为根的子树出现最多的颜色的编号之和。
思路:朴素的DSU ON TREE,开个桶记录就行,众数用set维护,当出现更大的,清空set,出现相等的,插入set即可。

#include<bits/stdc++.h>
#define pa pair<int,int>
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_backusing namespace std;inline ll read()
{ll f=1,sum=0;char c=getchar();while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}return sum*f;
}
const int MAXN=100010;
struct edge{int next,to;
}e[MAXN*2];
int head[MAXN],cnt;
void addedge(int u,int v)
{e[++cnt].next=head[u];e[cnt].to=v;head[u]=cnt;
}
int sz[MAXN],mxson[MAXN],mxsz[MAXN];
void pre_dfs(int x,int fa)
{for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa) continue;pre_dfs(v,x);sz[x]+=sz[v];if (sz[v]>mxsz[x]) mxson[x]=v,mxsz[x]=sz[v];}sz[x]++;
}
set <int> s;
int num[MAXN],nowmax,col[MAXN];
ll nowsum;
void del(int x,int fa)
{num[col[x]]--;for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa) continue;del(v,x);}
}
void add(int x,int fa)
{num[col[x]]++;if (num[col[x]]>nowmax){nowmax++;s.clear();s.insert(col[x]);nowsum=col[x];}else if (num[col[x]]==nowmax){nowsum+=col[x];s.insert(col[x]);}for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa) continue;add(v,x);}
}
ll ans[MAXN];
void dfs(int x,int fa,int save)
{for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa || v==mxson[x]) continue;dfs(v,x,0);}if (mxson[x]) dfs(mxson[x],x,1);num[col[x]]++;if (num[col[x]]>nowmax){nowmax++;s.clear();s.insert(col[x]);nowsum=col[x];}else if (num[col[x]]==nowmax){nowsum+=col[x];s.insert(col[x]);}for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (v==fa || v==mxson[x]) continue;add(v,x);}ans[x]=nowsum;if (!save) del(x,fa),nowsum=0,s.clear(),nowmax=0;
}
int main()
{int n=read();for (int i=1;i<=n;i++) col[i]=read();for (int i=1;i<n;i++){int u=read(),v=read();addedge(u,v),addedge(v,u);}pre_dfs(1,0);dfs(1,0,0);for (int i=1;i<=n;i++) cout<<ans[i]<<' ';cout<<endl;return 0;
}

相关文章:

DSU ON TREE

DSU ON TREE DSU&#xff1a;并查集 DSU ON TREE&#xff1a;树上启发式合并 我也不知道为啥树上并查集就是树上启发式合并 启发式合并的思想是每次把小的往大的合并&#xff0c;也就是最大化利用已有的答案&#xff08;大的数组不用清空&#xff0c;在原基础上加上小的即可&…...

Java“对象”

Java&#xff1a;PO、VO、BO、DO、DAO、DTO、POJO PO持久化对象&#xff08;Persistent Object&#xff09; PO是持久化对象&#xff0c;用于表示数据库中的实体或表的映射通常与数据库表的结构和字段对应PO的属性对应数据库表的字段&#xff0c;可以进行持久化操作&#xff0…...

vuepress+gitee免费搭建个人在线博客(无保留版)

文章目录 最终效果&#xff0c;一睹为快&#xff01;一、工具选型二、什么是VuePress三、准备工作3.1 node 安装3.2 Git安装3.3 Gitee账号注册 四、搭建步骤4.1 初始化VuePress4.2 安装VuePress4.3 初始化目录4.4 编写文章 五、部署到Gitee5.1 创建仓库5.2 个人空间地址设置4.3…...

Android 12.0 系统限制上网系列之iptables用IOemNetd实现app上网白名单的功能实现

1.前言 在12.0的系统rom定制化开发中,对于系统限制网络的使用,在system中netd网络这块的产品需要中,会要求设置app上网白名单的功能, liunx中iptables命令也是比较重要的,接下来就来在IOemNetd这块实现app上网白名单的的相关功能,就是在 系统中只能允许某个app上网,就是…...

Idea和DataGrip自定义常用代码模板,熟练使用快捷模板可促进开发效率

场景&#xff1a; 在实际工作中&#xff0c;我们不可能一个一个字母的去敲代码&#xff0c;为了提升开发效率&#xff0c;可以使用常用的快捷代码模板。idea和datagrip自带的有&#xff0c;我们也可以自定义快捷模板 一、Idea自定义代码模板、有些是基于 hutool 常用包 1、-&g…...

Vue.js :实现嵌套对话框的查看按钮

Vue.js &#xff1a;实现嵌套对话框的查看按钮 Vue.js 是一款流行的 JavaScript 框架&#xff0c;用于构建交互性强、响应式的前端应用程序。本博客将介绍如何使用 Vue.js 和 Element UI 库创建一个前端应用&#xff0c;其中包括了嵌套对话框的查看按钮&#xff0c;以及如何在…...

9.2.4 【MySQL】段的结构

段不对应表空间中某一个连续的物理区域&#xff0c;而是一个逻辑上的概念&#xff0c;由若干个零散的页面以及一些完整的区组成。像每个区都有对应的XDES Entry来记录这个区中的属性一样&#xff0c;定义了一个INODE Entry结构来记录段中的属性。 它的各个部分释义如下&#xf…...

怎么快速提取图片中的文字信息?怎么使用OCR图片文字提取一键提取文字

图片里的文字如何提取?一些图片中的文字信息是我们需要的&#xff0c;但是一个个输入太麻烦了&#xff0c;怎么将图片上的文字提取出来?Initiator是一款易于使用的小型 macOS OCR&#xff08;光学字符识别&#xff09;应用程序&#xff0c;可提取和识别 Mac 计算机屏幕上的任…...

Selenium隐藏浏览器特征

Selenium隐藏浏览器特征 Selenium特征1. CDP2. stealth.min.js3. undetected_chromedriver4. 操作已开启的浏览器4. 常见的隐藏Selenium特征的方法4.1 修改navigator.webdriver标志4.2 改变user-agent4.3 排除或关闭一些Selenium相关的开关4.4 代码展示4.5 总结 Selenium特征 …...

Linux下的buff/cache

目录 一、buff/cache二、buff/cache与内存管理三、buff/cache对系统性能的影响四、优化buff/cache1、调整vm.dirty_ratio和vm.dirty_background_ratio2、配置vm.swappiness3、配置vm.vfs_cache_pressure 五、释放buff/cache 一、buff/cache 按照Linux内核文档所说的&#xff0…...

3.wifi开发,网络编程

网络协议栈LwIP WiFi UDP Clinet编程 WiFi UDP Server编程 WiFi TCP Client编程 WiFi TCP Server编程 一。LWIP原理介绍&#xff0c;API介绍&#xff0c;文件结构 1.Lwip支持的协议 2.API 3.文件结构 1.api目录&#xff1a;应用程序接口文件。 2.arch目录&#xff1a;与硬件和…...

Android框架mqtt库无法兼容高版本android13的问题

最近使用mqtt库&#xff0c;测试的时候发现在Android12及以下正常&#xff0c;但在13上闪退&#xff0c;闪退日志如下 java.lang.IllegalArgumentException: com.yummo.xcar: Targeting S (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be spe…...

一招解除csdn复制限制

先看这个代码 python读取英文pdf翻译成中文pdf文件导出代码 想要复制代码&#xff0c;csdn有限制怎么办&#xff08;csdn流氓&#xff0c;无耻&#xff09; 解除方法 ctrlu 看效果...

安全基础 --- nodejs沙箱逃逸

nodejs沙箱逃逸 沙箱绕过原理&#xff1a;沙箱内部找到一个沙箱外部的对象&#xff0c;借助这个对象内的属性即可获得沙箱外的函数&#xff0c;进而绕过沙箱 前提&#xff1a;使用vm模块&#xff0c;实现沙箱逃逸环境。&#xff08;vm模式是nodejs中内置的模块&#xff0c;是no…...

Redis集群架构搭建——主从、哨兵、集群

上一篇文章Ubuntu上通过源码方式安装Redis已经介绍了如何安装redis&#xff0c;在这篇文章中&#xff0c;将会教大家搭建Redis的几种高可用的架构&#xff1a;主从架构、哨兵集群、Cluster集群。 本篇文章使用的redis版本为6.2.13&#xff0c;不同版本的配置可能有略微的区别&a…...

39 | selenium基础架构,UI测试架构

什么是测试基础架构&#xff1f; 测试基础架构指的是&#xff0c;执行测试的过程中用到的所有基础硬件设施以及相关的软件设施。因此&#xff0c;我们也把测试基础架构称之为广义的测试执行环境。通常来讲&#xff0c;测试基础架构主要包括以下内容&#xff1a; 执行测试的机器…...

2023研究生数学建模E题保姆级思路 出血性脑卒中临床智能诊疗

本次E题是一道J机器学习题目&#xff0c;难度也比较高&#xff0c;该题一般是有正确结果的&#xff0c;容易踩坑&#xff0c;不太建议小白选择&#xff0c;小白可以选择D题&#xff0c;D题思路也可以看另一篇文章&#xff0c;总的难度都不算低&#xff0c;这三道的难度接近&…...

画电路板通用知识

快捷键 快捷键 功能 shift+鼠标滚轮左右移动Ctrl+鼠标滚轮放大缩小 (Alt+) 鼠标滚轮上下移动滚轮按下鼠标滚轮可任意方向拖动图纸(可以一直保持按下状态或者按一下松开) CTRL+鼠标左键拖动复制该元件CTRL+E编辑选中元件的属性CTRL+鼠标左键 元叠选izoom in,聚焦光标所…...

三相组合式过电压保护器试验

三相组合式过电压保护器试验 试验目的 三相组合式过电压保护器主要分为有带串联间隙过压保护器和无间隙过压保护器两大类&#xff0c;其试验项目内容要求分别使用高压工频交流和高压直流电源。 三相组合式过电压保护器试验&#xff0c;主要是为了及早发现设备内部绝缘受潮及…...

C++提高编程:01 模板

这里写目录标题 1 模板的概念2 函数模板2.1 函数模板语法2.2 函数模板注意事项2.3 函数模板案例2.4 普通函数与函数模板的区别2.5 普通函数与函数模板的调用规则2.6 模板的局限性 3 类模板3.1 类模板语法3.2 类模板与函数模板区别3.3 类模板中成员函数创建时机3.4 类模板对象做…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

AD学习(3)

1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分&#xff1a; &#xff08;1&#xff09;PCB焊盘&#xff1a;表层的铜 &#xff0c;top层的铜 &#xff08;2&#xff09;管脚序号&#xff1a;用来关联原理图中的管脚的序号&#xff0c;原理图的序号需要和PCB封装一一…...

深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙

WebGL&#xff1a;在浏览器中解锁3D世界的魔法钥匙 引言&#xff1a;网页的边界正在消失 在数字化浪潮的推动下&#xff0c;网页早已不再是静态信息的展示窗口。如今&#xff0c;我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室&#xff0c;甚至沉浸式的V…...

Python第七周作业

Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt&#xff0c;并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径&#xff0c;并创建logs目录&#xff08;若不存在&#xff09; 3.递归遍历目录data&#xff0c;输出所有.csv文件的路径…...