当前位置: 首页 > 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 类模板对象做…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

沙箱虚拟化技术虚拟机容器之间的关系详解

问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西&#xff0c;但是如果把三者放在一起&#xff0c;它们之间到底什么关系&#xff1f;又有什么联系呢&#xff1f;我不是很明白&#xff01;&#xff01;&#xff01; 就比如说&#xff1a; 沙箱&#…...

Mac flutter环境搭建

一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...