【学习笔记】树上差分总结(点差分/边差分)

news2024/11/25 9:35:31

一.树上差分的基本概念

1.树上差分的定义

树上差分,顾名思义,意思就是在树上做差分

至于什么是差分呢?如果不会的同学,可以先看看我的这篇博客:一维,二维差分の详解(简单易懂)_一维差分-CSDN博客

2.树上差分能解决的问题

树上差分有什么作用?举个例子,如果题目要求对树上的一段路径进行操作,并询问某个点或某条边被经过的次数,树上差分就可以派上用场了。

类比于差分数组,树上差分利用的思想也是前缀和思想。(在这里应该是子树和思想

树上差分,就是利用差分的性质,对路径上的重要节点进行修改(而不是暴力全改),作为其差分数组的值,最后在求值时,利用dfs遍历求出差分数组的前缀和得出答案,就可以达到降低复杂度的目的。

树上差分时需要求LCA,不会的同学可以先看看我的这篇博客:详解最近公共祖先(LCA)-CSDN博客

树上差分一般有两种类型的题目,一种是对边进行差分,另一种就是对点进行差分

下面我将会分别讲解一下这两种问题。

二.点差分

1.思路

直接去dfs暴力加点权的话,肯定会TLE,但是我们现在在讲啥?树上差分啊!

假设需要将两点u,v之间路径上的所有点权增加x,l是lca(u,v),p是l的父亲节点,则差分操作如下:

sum[u] += x;
sum[v] += x;
sum[l] -= x;
sum[p] -= x;

举个栗子(其中假设x=1):

其中s和t就是题目中树上点权需要加1的节点的起始点,绿色的数字代表点权(已经加1了) 

则操作后有:

至于为什么要这么操作呢?别急,继续往下看。

做完上述的差分操作后,我们就要统计答案了。

当我们dfs搜索到s,向上回溯。

下面以u表示当前dfs搜索到的节点

对于每个u统计它的子树大小(要用前缀和的思想记录每个点的点权了),顺着路径标起来。

(即sum[u] += sum[son])

我们会发现第一次从s回溯到s与t的LCA时候,sum[LCA(s,t)] += sum[son[LCA(s,t)]]

此时sum[LCA(s,t)]=0(-1+1=0)。这时我们不禁会有一个疑问: "不是LCA(s,t)会被经过一次嘛,为什么是0!"

别急,我们继续搜另一边。.

继续:我们搜索到t,向上回溯。

依旧统计每个u的子树大小sum[u]+=sum[son]

再度回到LCA(s,t),依旧是sum[LCA(s,t)]+=sum[son[LCA(s,t)]]

这个时候 sum[LCA(s,t)]=1 这就达到了我们要的效果 (是不是特别优秀φ(゜▽゜*)♪)

但是我们还要思考一个问题:万一我们再从LCA(s,t)向上回溯的时候使得其父亲节点的子树和为1怎么办?这样我们不就使得其父亲节点被多经过了一次?

其实很简单,我们只需要在前面差分操作时将sum[fa[lca(s,t)]]-=x就行了。

这样就达到了标记我们路径上的点的要求! 是否有一种恍然大悟的感觉呢?

 

 2.例题Max flow

问题

参考代码

#include<bits/stdc++.h>
using namespace std;
int n,q,mx[300001][41],deep[300001],sum[300001],ans;
vector<int> vec[300001];
void dfs(int x,int fa)//lca的初始化
{
  deep[x] = deep[fa] + 1;
  mx[x][0] = fa;
  for(int i = 0;i < vec[x].size();i++)
    if(vec[x][i] != fa)
      dfs(vec[x][i],x);
}
int lca(int x,int y)//倍增法求lca
{
  if(deep[x] < deep[y]) swap(x,y);
  for(int i = 40;i >= 0;i--)
    if(deep[mx[x][i]] >= deep[y])
      x = mx[x][i];
  if(x == y) return x;
  for(int i = 40;i >= 0;i--)
    if(mx[x][i] != mx[y][i])
    {
      x = mx[x][i];
      y = mx[y][i];
	}
  return mx[x][0];
}
void dfss(int x,int fa)//统计答案的最大值
{
  for(int i = 0;i < vec[x].size();i++)
  {
  	int t = vec[x][i];
    if(t == fa) continue;
    dfss(t,x);
    sum[x] += sum[t];//在树上进行类似于前缀和的操作
  }
  ans = max(ans,sum[x]);//取最大值
}
signed main()
{
  cin>>n>>q;
  for(int i = 1;i < n;i++)
  {
  	int u,v;
  	scanf("%d%d",&u,&v);
  	vec[u].push_back(v);
  	vec[v].push_back(u);
  }
  dfs(1,0);
  for(int j = 1;j <= 40;j++)
    for(int i = 1;i <= n;i++)
      mx[i][j] = mx[mx[i][j - 1]][j - 1];
  while(q--)
  {
  	int u,v;
  	cin>>u>>v;
  	int l = lca(u,v);
	sum[u]++;//树上差分
	sum[v]++;
	sum[l]--;
	if(l != 1) sum[mx[l][0]]--;//如果l有父节点就进行后面的操作
  }
  dfss(1,0);
  cout<<ans;
  return 0;
}

三.边差分

1.思路

思想其实和点差分一样的,我来讲一下操作。

将两点s,t之间路径上的所有边权增加xl=LCA(s,t)以每条边两端深度较大的节点存储该边的差分数组,则操作如下:

sum[s] += x;
sum[t] += x;
sum[l] -= 2 * x;

举个栗子(其中假设x=1):

红色边为需要经过的边,绿色的数字代表经过次数

但是由于我们不能储存边权,所以只能把边权塞给了点权,因此我们的图应该是这样的

 

这样的话我们只要把sum[s]++,sum[t]++,sum[lca(s,t)]-=2就可以实现差分操作了。

同样地,只要dfs一遍,遍历时统计以每个节点为根的树的节点的权值和,就是当前节点到父亲节点的边的最终权值了!

 是不是很厉害

至于为什么点差分和边差分的操作不一样,很简单,请读者自己思考。

树上差分主要还是学习思想吧!

2.例题树上必经边--边差分

问题

 参考代码

#include<bits/stdc++.h>
using namespace std;
int n,q,mx[300001][41],deep[300001],sum[300001],ans,k;
vector<int> vec[300001];
void dfs(int x,int fa)
{
  deep[x] = deep[fa] + 1;
  mx[x][0] = fa;
  for(int i = 0;i < vec[x].size();i++)
    if(vec[x][i] != fa)
      dfs(vec[x][i],x);
}
int lca(int x,int y)
{
  if(deep[x] < deep[y]) swap(x,y);
  for(int i = 40;i >= 0;i--)
    if(deep[mx[x][i]] >= deep[y])
      x = mx[x][i];
  if(x == y) return x;
  for(int i = 40;i >= 0;i--)
    if(mx[x][i] != mx[y][i])
    {
      x = mx[x][i];
      y = mx[y][i];
	}
  return mx[x][0];
}
void dfss(int x,int fa)
{
  for(int i = 0;i < vec[x].size();i++)
  {
  	int t = vec[x][i];
    if(t == fa) continue;
    dfss(t,x);
    sum[x] += sum[t];
  }
}
signed main()
{
  cin>>n>>q>>k;
  for(int i = 1;i < n;i++)
  {
  	int u,v;
  	scanf("%d%d",&u,&v);
  	vec[u].push_back(v);
  	vec[v].push_back(u);
  }
  dfs(1,0);
    for(int j = 1;j <= 40;j++)
  for(int i = 1;i <= n;i++)
      mx[i][j] = mx[mx[i][j - 1]][j - 1];
  while(q--)
  {
  	int u,v;
  	cin>>u>>v;
  	int l = lca(u,v);
	  sum[u]++;
	  sum[v]++;
	  sum[l] -= 2;
  }
  dfss(1,0);
  for(int i = 2;i <= n;i++)
    if(sum[i] == k)
      ans++;
  cout<<ans;
  return 0;
}

四.BB in last 

如果这篇博客对您有帮助的话,别忘了点赞收藏加关注支持一下吖~(小声BB)(o゚v゚)ノ

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

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

相关文章

Unity动画循环偏移的使用

最近项目中有一个需求是做煤矿中猴车的动画&#xff0c;动画本身不复杂&#xff0c;但是猴车很多&#xff0c;怎么能简化工作量呢&#xff1f; 首先单个猴车的动画循环是必须要做的&#xff0c;重点是怎么让不同的猴车动画按顺序错开&#xff0c;研究了以下&#xff0c;可以通过…

Python爬虫从基础到入门:数据接口实战--获取豆瓣阅读热度最高的书籍信息

接着上一篇文章&#xff1a;Python爬虫从基础到入门&#xff1a;找数据接口&#xff0c;接下来实战一下&#xff0c;以获取豆瓣阅读这个网站热度最高的书籍信息为例&#xff0c;网址为&#xff1a;豆瓣阅读 Python爬虫从基础到入门&#xff1a;数据接口实战--获取豆瓣阅读热度…

APK签名 v1、 v2、v3、v3.1、v4 解析

在 Android 应用签名中&#xff0c;V1 V2 V3 V4签名是不同的签名方案&#xff0c;具体描述如下&#xff1a; V1 签名&#xff08;JAR 签名&#xff09;&#xff1a;早期 Android 应用签名的基本形式&#xff0c;基于 Java 签名&#xff08;JAR 签名&#xff09;规范。它将应用…

红日靶场1搭建渗透

环境搭建 下载好镜像文件并解压&#xff0c;启动vmware 这里我用自己的win7 sp1虚拟机作为攻击机&#xff0c;设置为双网卡NAT&#xff0c;vm2 其中用ipconfig查看攻击机ip地址 设置win7 x64为双网卡&#xff0c;vm1&#xff0c;vm2 设置win08单网卡vm1&#xff0c;win2k3为单…

基于微信小程序的家庭个人理财产品投资系统

家庭理财管理系统主要是为了提高工作人员的工作效率和更方便快捷的满足用户&#xff0c;更好存储所有数据信息及快速方便的检索功能&#xff0c;对系统的各个模块是通过许多今天的发达系统做出合理的分析来确定考虑用户的可操作性&#xff0c;遵循开发的系统优化的原则&#xf…

Docker Dockerfile

1、概念介绍 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。 每条保留字指令都必须为大写字母且后面要跟随至少一个参数 指令按照从上到下&#xff0c;顺序执行 #表示注释 每条指令都会创建一个新的镜像层并对镜像进…

机器学习周记(第二十八周:文献阅读-GSTPro)2024.1.29~2024.2.4

目录 摘要 ABSTRACT 1 论文信息 1.1 论文标题 1.2 论文摘要 1.3 论文背景 2 论文模型 2.1 问题描述 2.2 总体架构 2.3 动态图神经控制微分方程&#xff08;Dynamic Graph Neural Controlled Differential Equations&#xff09; 2.3.1 空间处理&#xff08;Spatial…

2024清洁能源、环境与智慧城市国际研讨会(ISCEESC2024)

2024清洁能源、环境与智慧城市国际研讨会(ISCEESC2024) 会议简介 2024年清洁能源、环境与智慧城市国际研讨会&#xff08;ISCEESC2024&#xff09;将在中国丽江举行。本次会议主要围绕清洁能源、环境和智慧城市等研究领域&#xff0c;旨在为该研究领域的专家学者提供一个国际…

怎么提取视频中的音频?多个方法告诉你(全)

在视频处理的日常中&#xff0c;我们经常会遇到想要单独保存或编辑视频中音频的情况。无论是为了制作音频片段、配乐&#xff0c;还是为了保存视频中的有趣对白&#xff0c;提取视频中的音频是一项实用的技能。怎么提取视频中的音频&#xff1f;本文将向您介绍多个简单而有效的…

放假“疯狂”玩电脑的护眼操作

引用原文&#xff1a;[Lenovo] 放假“疯狂”玩电脑的护眼操作 1. 开启夜间模式 有很多同学不知道&#xff0c;其实Win10/Win11系统本身就自带护眼模式&#xff0c;只要开启护眼模式&#xff0c;就能有效降低屏幕对眼睛的伤害。&#xff08;以下以Win10系统操作步骤为例&#xf…

再识C语言 DAY13 【递归函数(超详细)】

文章目录 前言一、函数递归什么是递归递归的两个重要条件练习一练习二 递归与迭代练习三练习四在练习三、四中出现的问题 如果您发现文章有错误请与我留言&#xff0c;感谢 前言 本文总结于此文章 一、函数递归 什么是递归 函数调用自身的编程技巧称为递归 &#xff08;函数自…

【机器学习】全网最全模型评价指标(性能指标、YOLOv5训练结果分析、轻量化指标、混淆矩阵详解)【基础收藏】

&#x1f951; Welcome to Aedream同学 s blog! &#x1f951; 文章目录 模型性能指标常见指标ROC/AUCROC & PRC多分类问题——混淆矩阵 计算结果分析——以YOLO v5为例1. confusion_matrix.png(混淆矩阵)2. F1_curve&#xff1a;3. labels.jpg4. labels_corrrelogram.jpg5…

STM32外部中断(红外传感器与旋转编码器计数案例)

文章目录 一、介绍部分简介中断系统中断执行流程STM32中断NVIC基本结构NVIC优先级分组外部中断外部中断简介外部中断基本结构外部中断的流程AFIOEXTI框图 相关外设介绍旋转编码器介绍硬件电路对射式红外传感器 二、代码实现对射式红外传感器计次连接电路封装红外传感器与中断函…

清华系2B模型杀出,性能吊打LLaMA-13B

2 月 1 日&#xff0c;面壁智能与清华大学自然语言处理实验室共同开源了系列端侧语言大模型 MiniCPM&#xff0c;主体语言模型 MiniCPM-2B 仅有 24 亿&#xff08;2.4B&#xff09;的非词嵌入参数量。 在综合性榜单上与 Mistral-7B 相近&#xff0c;在中文、数学、代码能力表现…

怎么把物品信息图片批量生成二维码?每张图片单独生码的制作技巧

现在通过扫码来查看人员或者物品信息的方式越来越常见&#xff0c;在合适的位置放置对应的二维码内容&#xff0c;让其他人通过扫码来获取图片信息。那么如果我们将每个信息做成一张图片后&#xff0c;需要将图片生成二维码时&#xff0c;有能够批量生成二维码的方法可以快速处…

天地伟业接入视频汇聚/云存储平台EasyCVR详细步骤

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

基于深度卷积神经网络的图像配准(DeepSlice)

文章目录 一、基于DeepSlice的切片配准1.1、研究现状1.2、网络模型&#xff08;DeepSlice&#xff09;1.3、优化策略1.3.1、开发了一个基准数据集&#xff08;GT&#xff09;1.3.2、构建了阶段二的训练数据集&#xff08;增强训练&#xff09;1.3.3、角度集成 切割索引&#x…

[ChatGPT们】ChatGPT 如何辅助编程初探

主页&#xff1a;元存储的博客 全文 9000 字&#xff0c; 原创请勿转载。 我没有写过诗&#xff0c;但有人说我的代码像诗一样优雅 -- 雷军 图片来源&#xff1a;https://www.bilibili.com/video/BV1zL411X7oS/ 1. 引言 作为一个程序员&#xff0c;我们不仅要熟悉各种编程语…

ctfshow web入门 1-2 +密码签到

web1 F12源代码 web2 ctrlu cypto 签到 a}wohs.ftc{galf print(a[::-1])

使用 LoRA 在 vi​​ggo 数据集上微调 Microsoft phi-2 小语言模型

一、说明 Microsoft 的基于 Transformer 的小语言模型。它可以根据 MIT 许可在HuggingFace上使用。 它在 96 个 A100 GPU 上使用 1.4T 令牌进行了 14 天的训练。Phi-2 是一个 27 亿个参数的预训练 Transformer&#xff0c;不使用 RLHF 或指示微调。它进行下一个标记预测&#x…