LCA 树上差分(点差分 , 边差分)

news2024/9/30 5:28:45

文章目录

  • 1. LCA(求最近公共父节点 , 求树上两点最短距离)
    • 先求节点深度 , 处理 fa 数组 , 然后做LCA过程
    • 板子(有根树 , 无根树默认 1 为根即可)
      • 1.Dis(求树上两点最近距离)
      • 2.聚会
  • 树上差分
    • 用来处理树上的一些区间操作 , 一般和LCA一起考察
    • 树上点差分 , 对树上两节点间点权值处理 :
      • 1. 松鼠的新家
    • 树上边差分 , 对树上两节点间边权值处理 :
      • 1. 暗的连锁

1. LCA(求最近公共父节点 , 求树上两点最短距离)

先求节点深度 , 处理 fa 数组 , 然后做LCA过程

板子(有根树 , 无根树默认 1 为根即可)

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long ll;
const int N = 1e6+10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;
const int inf = 1 << 31 - 1;
const double eps = 1e-9;

vector<int>ve[N];
int n , m , s , d[N] , f[N][21]; // 第 i 个点的前 (1 << j) 个祖先是谁

void dfs(int x,int fa){
	
	f[x][0] = fa;
	for(int i=1;(1<<i)<=d[x];i++) f[x][i] = f[f[x][i-1]][i-1];//更新 st 表
	/*
		注意这里一定要写 小于等于(<=) 避免粗心导致忘记初始化 d[root] = 1
		写小于等于两种情况都合适
	*/
	
	for(auto k : ve[x]){
		if(k == fa) continue;
		d[k] = d[x] + 1;dfs(k,x);
	}//更新深度
}

int lca(int x,int y){
	if(d[x] < d[y]) swap(x , y);//d[x] 深度大
	for(int i=20;i>=0;i--) if(d[x] - (1 << i) >= d[y])	x = f[x][i];//跳到相同位置
	if(x == y) return x;//到达同一个点
	for(int i=20;i>=0;i--){
		if(f[x][i] != f[y][i]){
			x = f[x][i];
			y = f[y][i];
		}
	}//往上跳
	return f[x][0];
}



int main(){


	IOS

	cin >> n >> m >> s;
	for(int i=1;i<n;i++){
		int u , v;
		cin >> u >> v;
		ve[u].push_back(v);
		ve[v].push_back(u);		
	}//建边
	
	dfs(s,0);//建立 st 表和深度 
	
	for(int i=1;i<=m;i++){
		int u , v;
		cin >> u >> v;
		cout << lca(u,v) << "\n";
	}//求lca
	
	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

1.Dis(求树上两点最近距离)

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long ll;
const int N = 1e5+10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;
const int inf = 1 << 31 - 1;
const double eps = 1e-9;

int n , m , root;
vector<PII>ve[N];
int f[N][30] , d[N] , disx[N][30];

void dfs(int x,int fa,int dis){
	
	f[x][0] = fa;disx[x][0] = dis;
	for(int i=1;(1<<i)<=d[x];i++) f[x][i] = f[f[x][i-1]][i-1] , disx[x][i] = disx[x][i-1] + disx[f[x][i-1]][i-1];
	
	for(auto [y,l] : ve[x]){
		if(y == fa) continue;
		d[y] = d[x] + 1;dfs(y , x , l);
	}
}

int lca(int x,int y){
	int ans = 0;
	if(d[x] < d[y]) swap(x , y);
	for(int i=20;i>=0;i--) if(d[x] - (1 << i) >= d[y]) ans += disx[x][i] , x = f[x][i] ;
	if(x == y) return ans;
	for(int i=20;i>=0;i--){
		if(f[x][i] != f[y][i]){
			ans += disx[x][i] + disx[y][i];
			x = f[x][i];
			y = f[y][i]; 
		}
	}
	return ans + disx[x][0] + disx[y][0];
}

int main(){
	
	IOS
	
	cin >> n >> m;
	for(int i=1;i<n;i++){
		int u , v , w;
		cin >> u >> v >> w;
		ve[u].push_back({v,w});
		ve[v].push_back({u,w});
	}
	dfs(1 , 0 , 0);
	
	for(int i=1;i<=m;i++){
		int u , v;
		cin >> u >> v;
		cout << lca(u,v) << "\n";
	}


	
	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

2.聚会

洛谷P4281 聚会
问题:求树上三个点到达同一个点的最小距离 , 和这个点的编号
结论:三个点两两之间求lca一定有两个相同 , 不相同的那个就是要求点,最小距离等于两两之间距离和的一半

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long ll;
const int N = 5e5+10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;
const int inf = 1 << 31 - 1;
const double eps = 1e-9;

int n , m , root;
vector<int>ve[N];
int cnt[N] , f[N][21] , d[N][21] , h[N];
int a , b , c;

void dfs(int x,int fa,int l){
	
	f[x][0] = fa;d[x][0] = l;
	for(int i=1;(1<<i)<=h[x];i++){
		f[x][i] = f[f[x][i-1]][i-1];d[x][i] = d[x][i-1] + d[f[x][i-1]][i-1];
	} 	
	for(auto k : ve[x]){
		if(k == fa) continue;
		h[k] = h[x] + 1;dfs(k,x,1);
	}
	
}

int lca(int x,int y,int &t){
	
	int ans = 0;
	
	if(h[x] < h[y]) swap(x , y);
	for(int i=20;i>=0;i--) if(h[x] - (1 << i) >= h[y]) ans += d[x][i] , x = f[x][i]; 
	if(x == y){	t = x;	return ans; }
	
	for(int i=20;i>=0;i--){
		if(f[x][i] != f[y][i]){
			ans += d[x][i] + d[y][i];
			x = f[x][i];y = f[y][i];
		}
	}
	t = f[x][0];
	return ans + d[x][0] + d[y][0];
	
}

int r(int x,int y,int z){
	if(x == y) return z;
	else if(x == z) return y;
	else return x;
}

int main(){

	IOS
	
	cin >> n >> m;
	for(int i=1;i<n;i++){
		int u , v;
		cin >> u >> v;
		ve[u].push_back(v);
		ve[v].push_back(u);
		cnt[u]++;cnt[v]++;
	}
	for(int i=1;i<=n;i++) if(cnt[i] == 1) root = i;
	dfs(root , 0 , 0);
	
	
	for(int i=1;i<=m;i++){
		int u , v , w , dd;
		cin >> a >> b >> c;
		dd = (lca(a,b,u) + lca(b,c,v) + lca(c,a,w)) / 2 ;
		cout << r(u,v,w) << " " << dd << "\n";
	}
	
	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

树上差分

用来处理树上的一些区间操作 , 一般和LCA一起考察

树上点差分 , 对树上两节点间点权值处理 :

两个节点 u , v
w = lca(u , v) 
x = f[w][0];
c[u]++;
c[v]++;
c[w]--;
c[x]--;

1. 松鼠的新家

洛谷 P3258 松鼠的新家

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long ll;
const int N = 3e5+10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;
const int inf = 1 << 31 - 1;
const double eps = 1e-9;

int n , a[N];
vector<int>ve[N];
int f[N][21] , d[N] , c[N];

void dfs(int x,int fa){
	f[x][0] = fa;
	for(int i=1;(1<<i)<=d[x];i++) f[x][i] = f[f[x][i-1]][i-1];
	for(auto k : ve[x]){
		if(k == fa) continue;
		d[k] = d[x] + 1;dfs(k,x);
	}
}

int lca(int x,int y){
	if(d[x] < d[y]) swap(x,y);
	for(int i=20;i>=0;i--) if(d[x] - (1<<i) >= d[y]) x = f[x][i];
	if(x == y) return x;
	for(int i=20;i>=0;i--){
		if(f[x][i] != f[y][i]){
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}

void dfs1(int x,int fa){
	for(auto k : ve[x]){
		if(k == fa) continue;
		dfs1(k,x);
		c[x] += c[k];
	}
}

int main(){

	IOS

	cin >> n;
	for(int i=1;i<=n;i++) cin >> a[i];
	for(int i=1;i<n;i++){
		int u , v;
		cin >> u >> v;
		ve[u].push_back(v);
		ve[v].push_back(u);
	}
	
	dfs(1 , 0);
	
	for(int i=1;i<n;i++){
		int u = a[i] , v = a[i+1];
		int w = lca(u , v) , x = f[w][0];
		c[u]++;c[v]++;c[w]--;c[x]--;
	}
	
	dfs1(1,0);
	
	for(int i=2;i<=n;i++) c[a[i]]--;//除了第一个点之外的点都被重复计数一次
	//注意a[i] 存的是点信息 , c[i]--; 是错误的
	for(int i=1;i<=n;i++) cout << c[i] << "\n";



	
	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

树上边差分 , 对树上两节点间边权值处理 :

用一个边的子节点代表这个边(无根树以 1 为根 , 那么 1 的差分数组是无意义的)
两个节点 u , v
w = lca(u , v) 
x = f[w][0];
c[u]++;
c[v]++;
c[w]-= 2;

1. 暗的连锁

思路 :对于每一条附加边 , 它可以在树上生成一条环 , 那么这条环上的每一条主要边都需要砍掉这一条附加边才能断开 , 每一条附加边都影响自己所构成的环 , 最后 , 受到影响 0 次的主要边可以砍掉任意附加边断开 , 受到影响一次的主要边只能砍影响他的附加边 , 影响一次及以上的无法断开
计数的时候做树上边差分即可

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
typedef long long ll;
const int N = 1e5+10;
const int mod = 1e9 + 7;
typedef pair<int,int>PII;
const int inf = 1 << 31 - 1;
const double eps = 1e-9;

int n , m , ans;
vector<int>ve[N];
int f[N][21] , d[N] , c[N];

void dfs(int x,int fa){
	f[x][0] = fa;
	for(int i=1;(1<<i)<=d[x];i++) f[x][i] = f[f[x][i-1]][i-1];
	for(auto k : ve[x]){
		if(k == fa) continue;
		d[k] = d[x] + 1;dfs(k,x);
	}
}

int lca(int x,int y){
	if(d[x] < d[y]) swap(x,y);
	for(int i=20;i>=0;i--) if(d[x] - (1<<i) >= d[y]) x = f[x][i];
	if(x == y) return x;
	for(int i=20;i>=0;i--){
		if(f[x][i] != f[y][i]){
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}

void dfs1(int x,int fa){
	for(auto k : ve[x]){
		if(k == fa) continue;
		dfs1(k , x);
		c[x] += c[k];
	}
	
	if(c[x] == 0 && x != 1) ans += m;
	else if(c[x] == 1) ans += 1;
}

int main(){

	IOS
	cin >> n >> m;
	for(int i=1;i<n;i++){
		int u , v;
		cin >> u >> v;
		ve[u].push_back(v);
		ve[v].push_back(u);
	}
	
	dfs(1,0);
	
	for(int i=1;i<=m;i++){
		int u , v , w;
		cin >> u >> v;
		w = lca(u,v);
		c[u]++;c[v]++;c[w] -= 2;
	}
	
	dfs1(1,0);
	
	cout << ans;
	
	
	

	
	return 0;
}
//freopen("文件名.in","r",stdin);
//freopen("文件名.out","w",stdout);

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

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

相关文章

ElasticSearch配置SearchGuard

一、安装ElasticSearch6.4.3 下载ElasticSearch6.4.3 1、解压到/usr/local/elasticsearch-6.4.3 tar -zxvf elasticsearch-6.4.3.tar.gz 2、修改配置文件elasticsearch.yml cluster.name: searchguard_demo node.name: node123 network.host: 0.0.0.0 2、创建linux用户es…

百度搜索去广告及高级用法

更高级的B站视频 6个百度精准搜索的技巧&#xff01;还可以屏蔽广告哦~&#xff08;搜索指令教程&#xff09;_哔哩哔哩_bilibili 1.去广告最简单的指令 搜索问题之后 空格 之后-advertisement 2.精确搜索 我们经常在搜索比较长的短句的时候会发现&#xff0c;百度会把…

密码学|DES加密算法|学习记录

DES简介 DES属于对称密码算法中的分组加密算法 密钥一共64bit&#xff0c;其中56位参与运算&#xff0c;其余8bit为校验位&#xff08;8 16 24 32 40 48 56 64&#xff09; n个64位明块经过加密后得到的n个64位密文块加在一起就是密文 DES一般步骤 IP置换 &#xff1a; IP置…

【ChatGPT】ChatGPT掀起AIGC与AI浪潮

文章目录 前言 一、我为什么要这么做&#xff1f; 二、AI与AIGC 1.AI是什么&#xff1f; 2. AIGC是什么&#xff1f; 2.1 AIGC的优势 2.2 AIGC的劣势 3. AI与AIGC的区别 三、ChatGPT 四、应对措施和改变 1. 找到自己的风格 2. 学习AI的优点 3. 创新型方法 总结​​​​​​​ 前…

Mybatis分页查询——四种传参方式

目录 一、顺序传参 1. 持久层接口方法 2. UserMapper.xml映射文件新增标签 3. 新增测试方法 4. 运行结果 二、param传参 1. 持久层接口方法 2. UserMapper.xml映射文件新增标签 3. 新增测试方法 4. 运行结果 三、自定义POJO类传参 1. 自定义POJO类 2. 持久层接口方…

深度学习中的学习率设置技巧与实现详解

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

【计算机网络】第二章 应用层 3

Email应用的特性 异步应用&#xff0c;方便用户 提供一对多通信 价格低廉 主要包含: o 用户代理(user agents,UA) o 邮件服务器(mail servers) o 邮件传输协议&#xff1a;SMTP o 邮件访问协议&#xff1a;POP3或IMAP 用户代理 o 客户端程序 o 提供编辑、发…

【Linux】认识协议

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

【Java 编程语言】——JDK 安装

JDK 安装 文章目录JDK 安装一、JDK的选择与下载1.JDK的选择2.JDK的下载二、Java环境变量的配置一、JDK的选择与下载 1.JDK的选择 目前的JDK的版本更新很快&#xff0c;已经到了JDK20了。但是对于普通的开发或者学习人员来说&#xff0c;选择较为稳定的JDK是更为合适的选择。当…

干货丨AI常见问题及处理方法

AI软件在运行时经常会容易报错或者操作不成功&#xff0c;问题及处理方法分享给大家 01 当AI中色板里面没有颜色可选 原因&#xff1a;将图片素材直接以新窗口打开&#xff0c;所以显示的是位图文件。 解决办法&#xff1a;重新新建文件&#xff0c;然后将图片拖入新建文件中…

让Ai来告诉你Linux应该怎么学

今天在slack上添加了Claude&#xff0c;他属于ChatGPT的最强竞品&#xff0c;支持中文&#xff0c;体验非常舒适&#xff0c;也并不像国内某些自建AI那样弱智。 至于Linux要怎么学&#xff0c;就让Claude来回答吧。 你能告诉我Liunx应该怎么学吗&#xff1f; 学习Linux,我有…

Elasticsearch:使用 Elastic APM 监控 Android 应用程序

作者&#xff1a;Alexander Wert, Cesar Munoz 人们通过私人和专业的移动应用程序在智能手机上处理越来越多的事情。 拥有成千上万甚至数百万的用户&#xff0c;确保出色的性能和可靠性是移动应用程序和相关后端服务的提供商和运营商面临的主要挑战。 了解移动应用程序的行为、…

【Mysql系列】——详细剖析数据库中的存储引擎

【Mysql系列】——详细剖析数据库中的存储引擎&#x1f60e;前言&#x1f64c;存储引擎什么是存储引擎&#xff1f;Mysql的体系结构&#xff1a;Mysql的体系结构分为四层&#xff1a;连接层服务层引擎层存储层存储引擎的查看存储引擎的指定存储引擎的特点InnoDB介绍InnoDB特点I…

论文浅尝 | 大语言模型在in-context learning中的不同表现

笔记整理&#xff1a;毕祯&#xff0c;浙江大学博士&#xff0c;研究方向为知识图谱、自然语言处理链接&#xff1a;https://arxiv.org/pdf/2303.03846.pd本文是谷歌等机构最新发表的论文&#xff0c;旨在研究大模型上下文学习的能力。这篇论文研究了语言模型中的上下文学习是如…

数影周报:现代汽车发生数据泄露事件;淘宝天猫集团完成组织调整

本周看点&#xff1a;现代汽车发生数据泄露事件&#xff1b;微软会议应用Teams 新功能可禁用/启用脏话过滤器&#xff1b;欧洲隐私监管机构创建ChatGPT工作组&#xff1b;淘宝天猫集团完成组织调整&#xff1b;阿里巴巴再向Lazada投资3.529亿美元...... 数据安全那些事 现代汽车…

C语言数据结构-队列的知识总结归纳

队列的知识总结归纳一.队列的基本概念二.循环队列的顺序存储常见的基本操作以及详细图解1.队列的顺序存储结构类型定义2.初始化队列初始化队列示意图3.判断队空4.判断队列是否满的三种方法图示5.入队或进队入队的示意图6出队或退队出队的图示三. 队列的链式存储结构四. 链式队列…

AutoGPT自主人工智能用法和使用案例

介绍 AutoGPT是什么&#xff1a;自主人工智能&#xff0c;不需要人为的干预&#xff0c;自己完成思考和决策【比如最近比较热门的用AutoGPT创业&#xff0c;做项目–>就是比较消耗token】 AI 自己上网、自己使用第三方工具、自己思考、自己操作你的电脑【就是操作你的电脑…

缺省函数,函数重载,引用简单介绍的补充说明

TIPS 命名空间域的作用实际上相当于把部分变量的名称给他隔离起来&#xff0c;这样的话就可以减少变量名的冲突。命名空间是对全局域当中的这些变量啊&#xff0c;函数啊&#xff0c;类型啊进行一个封装与隔离&#xff0c;可以防止你和我之间的冲突&#xff0c;也可以防止与库…

leetcode:各位相加(数学办法详解)

前言&#xff1a;内容包括&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路 目录 题目&#xff1a; 代码实现&#xff1a; 大致思路&#xff1a; 题目&#xff1a; 给定一个非负整数 num&#xff0c;反复将各个位上的数字相加&#xff0c;直到结果为一位数。返回…

【云原生Docker】11-Docker镜像仓库

【云原生|Docker】11-Docker Registry(官方仓库) 文章目录【云原生|Docker】11-Docker Registry(官方仓库)前言docker registry简介操作示例hyper/docker-registry-web前言 ​ 前面我们所有的docker操作&#xff0c;使用的镜像都是在docker官方的镜像仓库下载&#xff0c;当然这…