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;
}

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

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

相关文章

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;与硬件和…

机器学习笔记:Huber Loss

1 介绍 Huber Loss是回归问题中的一种损失函数&#xff0c;它结合了均方误差MSE和绝对误差MAE的特点。 Huber Loss在误差较小的时候是平方损失&#xff0c;而在误差较大的时候是线性损失。因此&#xff0c;它在处理有噪声的数据时&#xff0c;尤其是存在离群点的情况下&#x…

nginx反向代理vue项目

文章目录 前言一、创建站点1.添加站点2.添加ssl证书 二、反向代理vue项目1.添加反向代理2.更改vue项目配置3.修改反向代理配置 前言 项目描述&#xff1a;前端vue项目、后端Java项目、首页WordPress项目 客户要求&#xff1a;使用宝塔进行部署 需求描述&#xff1a;客户只有一…

一文彻底理解synchronized(通俗易懂的synchronized)

目录 一、什么是synchronized 二、synchronized的四种用法 2.1、修饰一个代码块 2.2、修饰一个方法 2.3、修饰一个静态的方法 2.4、修饰一个类 三、使用案例分析 3.1、修饰一个代码块 3.2、修饰一个方法 3.3、修饰一个静态的方法 3.4、修饰一个类 3.5 经典用法&…

蓝桥杯 题库 简单 每日十题 day8

01 扫雷 题目描述 在一个n行列的方格图上有一些位置有地雷&#xff0c;另外一些位置为空。 请为每个空位置标一个整数&#xff0c;表示周围八个相邻的方格中有多少个地雷。 输入描述 输入的第一行包含两个整数n&#xff0c;m。 第2行到第n1行每行包含m个整数&#xff0c;相邻整…

Winform直接与Wpf交互

Winform项目中&#xff0c;可以直接使用wpf中的自定义控件和窗体 测试环境&#xff1a; vistual studio 2017 window 10 一 winform直接使用wpf的自定义控件 步骤如下&#xff1a; 1 新建winfrom项目&#xff0c;名为WinFormDemo&#xff0c;默认有一个名为Form1的窗体…

OpenAI官方吴达恩《ChatGPT Prompt Engineering 提示词工程师》(4)推理/Inferring

推理/Inferring 推理是指模型将文本作为输入并执行某种分析的任务。这可以是提取标签、提取名称、理解文本的情感等任务。 例如&#xff0c;如果您想从一段文本中提取积极或消极的情感&#xff0c;在传统的机器学习工作流程中&#xff0c;您需要收集标签数据集、训练模型、确定…

关于表单快速开发低代码技术平台的内容介绍

运用什么样的表单快速开发软件平台可以实现高效率创收&#xff1f;随着科技的进步和飞速发展&#xff0c;专业的低代码技术平台已经走入了很多企业的办公职场中&#xff0c;它们灵活、轻量级、优质、高效、易维护等优势特点&#xff0c;可以高效助力广大企业提质增效&#xff0…

位移贴图的实现原理

在以前的文章中介绍过GLTF编辑器 &#xff0c; 编辑器可以对模型的各种材质纹理进行编辑修改&#xff0c;但是有一些新手用户可能对这些材质纹理不太了解&#xff0c;所以我收集了一些资料对这些材质纹理做一下详细的介绍&#xff0c;今天这篇文章主要是介绍位移贴图。 1、什么…

stm32之智能垃圾桶实战

之前用过51做过一个垃圾桶的小项目&#xff0c;这里用32重新搞了一下。视频的效果和之前一样&#xff0c;可参考这个垃圾桶效果 。 一、项目描述&#xff08;同51&#xff09; 项目主要是模拟不用手动打开垃圾桶盖&#xff0c;而进行自动操作。自动打开的条件如下&#xff1a…

【二叉树魔法:链式结构与递归的纠缠】

本章重点 二叉树的链式存储二叉树链式结构的实现二叉树的遍历二叉树的节点个数以及高度二叉树的创建和销毁二叉树的优先遍历和广度优先遍历二叉树基础oj练习 1.二叉树的链式存储 二叉树的链式存储结构是指&#xff0c;用链表来表示一棵二叉树&#xff0c;即用链来指示元素的逻辑…

OpenAI官方吴达恩《ChatGPT Prompt Engineering 提示词工程师》(3)摘要

摘要/ Summarizing 如何使用大模型来概括文本 环境准备 和&#xff08;①指南&#xff09;一样需要搭建一个环境 导入OpenAI、加载API密钥以及这个getCompletion辅助函数 import openai import osfrom dotenv import load_dotenv, find_dotenv _ load_dotenv(find_dotenv(…

点云从入门到精通技术详解100篇-单期点云的高斯曲率定位桥梁潜在损伤技术研究

目录 前言 国内外研究现状 三维激光扫描对桥梁损伤检测的研究现状 基于点云高斯曲率损伤检测的研究现状 柱体偏差检测技术研究现状 存在的问题 法向量约束高斯曲率的 TLS 桥面潜在损伤区域探测 2.1 高斯曲率探伤的基本理论 2.2 点云拓扑关系建立的方法比较 2.2.1 KD-…

OpenCV项目开发实战--进行高动态范围 (HDR) 成像--附C++与Python的实现源码

在本教程中,我们将学习如何使用以不同曝光设置拍摄的多张图像创建高动态范围 (HDR) 图像。本文最后将共享 C++ 和 Python 代码供读者下载。 什么是高动态范围 (HDR) 成像? 大多数数码相机和显示器都以 24 位矩阵的形式捕获或显示彩色图像。每个颜色通道有 8 位,因此每个通…

社区分享|MeterSphere变身“啄木鸟”,助力云帐房落地接口自动化测试

云帐房网络科技有限公司&#xff08;以下简称为“云帐房”&#xff09;成立于2015年3月&#xff0c;以“成为最值得信赖的税务智能公司”为愿景&#xff0c;运用人工智能、大数据等互联网技术&#xff0c;结合深厚的财税行业服务经验&#xff0c;为代账公司和中大型企业提供智能…

【2023年中国研究生数学建模竞赛华为杯】E题 出血性脑卒中临床智能诊疗建模 问题分析、数学模型及代码实现

【2023年中国研究生数学建模竞赛华为杯】E题 出血性脑卒中临床智能诊疗建模 1 题目 1.1 背景介绍 出血性脑卒中指非外伤性脑实质内血管破裂引起的脑出血&#xff0c;占全部脑卒中发病率的10-15%。其病因复杂&#xff0c;通常因脑动脉瘤破裂、脑动脉异常等因素&#xff0c;导致…

力扣450 补9.15

450.删除二叉搜索树中的节点 可以做&#xff0c;就是去分类讨论&#xff0c;一开始以为有重复节点&#xff0c;感觉挺麻烦的&#xff0c;不过没有重复结点&#xff0c;感觉好做一点了&#xff0c;不过写结点指针赋值的时候把值赋反了&#xff0c;搞得一直报错&#xff0c;还是指…

Spring的RestTemplate、RPC和HTTP

https://blog.csdn.net/weixin_35674711/article/details/96112328 1 . 目标 理解RPC和HTTP的区别 能使用RestTemplate发送请求 2. 讲解 1 . RPC和HTTP 常见远程调用方式&#xff1a; RPC:(Remote Produce Call)远程过程调用 1.基于Socket 2.自定义数据格式 3.速度快&am…

黑马JVM总结(十九)

&#xff08;1&#xff09;GC调优1 通过官网查看查看JVM的参数&#xff1a; 可以使用java命令查看当前环境下的虚拟机参数&#xff1a; 学会使用一些工具如前面学的jmap &#xff0c;jconsole等等工具 &#xff08;2&#xff09;GC调优2 垃圾回收调优只是众多调优中的一个方…

读高性能MySQL(第4版)笔记14_备份与恢复(中)

1. 在线备份 2. 离线备份 2.1. 关闭MySQL做备份是最简单、最安全的 2.2. 所有获取一致性副本的方法中最好的 2.3. 损坏或不一致的风险最小 2.4. 根本不用关心InnoDB缓冲池中的脏页或其他缓存 2.5. 不需要担心数据在尝试备份的过程中被修改 2.5.1. 服务器不对应用提供访问…