首页 > 其他分享 >虚树 学习笔记

虚树 学习笔记

时间:2024-09-26 21:35:17浏览次数:1  
标签:int top 笔记 son 学习 LCA 虚树 关键点

虚树 Virtual Tree 学习笔记

引入

P2495 [SDOI2011] 消耗战

题目大意:给一棵 \(n\) 个点的树,\(m\) 次询问 \(k\) 个点,要求切断一些边使点 1 不可达这些点,求最小切断的边权和。

\(n\le 2.5*10^5,m\le 5*10^5,\sum k\le 5*10^5\)


先考虑一个朴素的 DP,每次询问扫一遍整个树。

设 \(f_i\) 为使点 \(i\) 的子树内的关键点不与 1 连通的最小花费,

\(g_i\) 为 1 到 \(i\) 路径上的边权的最小值。

若 \(i\) 为查询的点,\(f_i=g_i\)。

否则 \(f_i=\min(g_i,\sum f_{son_i})\)。

时间复杂度 \(O(nQ)\)。


发现 \(\sum k\) 与 \(n\) 同阶,所以一次查询里的关键点很少,很多点都是没有用的。

这时可以用虚树来优化。

什么是虚树?

虚树常见于树形 DP 中。

如果一次询问所包含的关键点很少(关键点可以理解为对答案有实际影响的点),即有很多点都是多余无用的。

如果每次我们还是暴力地扫一遍整棵树,那是 \(O(nQ)\) 的。

我们考虑把关键点及其有关的点取出来,按照原树的祖先后代关系新建一棵树,这就是虚树。

如果所有询问的关键点总数与 \(n\) 同阶,则最后复杂度就是 \(O(n+Q)\) 了。

如何建一棵虚树?

前置知识

我们需要一个高效的 LCA 算法;

\(O(\log n)\) 可以用常数更小的树剖代替倍增。

\(O(n\log n)\) 预处理,\(O(1)\) 查询的 RMQ 也可。


选点

显然我们需要的点是那些关键点和他们之间的 LCA。

根据前人的智慧加上我自己的感性理解,先把所有关键点按 dfs 序排好然后相邻两个求 LCA。

这些 LCA + 根节点 + 所有 \(k\) 个关键点 = 我们所需的点。

所有点的个数就是 \(O(k)\) 的。

连边

先把所有点按 dfs 序排序,然后去重。

我们用一个单调栈维护虚树上由根节点出发的一条链。

开始把根节点 1 入栈。

之后遍历排好序的点。

设当前点为 \(x=d_i\) 与栈顶为点 \(top\)。

若 \(LCA(x,top)=top\),则点 \(x\) 仍在 \(1\rightarrow top \rightarrow x\) 这条链上,则连边 \((top,x)\),将 \(x\) 直接入栈。

若 \(y=LCA(x,top)\neq top\),则点 \(x\) 不在 \(1\rightarrow top\) 这条链上了,\(y\) 即为这条链与链 \(1\rightarrow x\) 的交叉点。

\(y\) 显然也是我们选的点之一,此时一直退栈直到栈顶为 \(y\),然后连边 \((y,x)\),将 \(x\) 入栈。

回顾连边

当 \(d_i\) 连完边后,事实上我们的栈顶一直都为 \(d_i\)。

而与 \(d_i\) 连边的点为栈顶 \(LCA(top,d_i)\),即 \(LCA(d_{i-1},d_i)\)。

也就是说在排完序后,只需连 \((LCA(d_{i-1},d_i),d_i)\) 即可,看起来十分简洁,其中要连 \((1,d_1)\)。

也就是说我们实际并不需要一个栈来维护链。

总结

  1. 把关键点按 dfs 序排序。
  2. 关键点相邻两个求 LCA。
  3. 所有点(LCA + 关键点)按 dfs 序排序。
  4. 遍历数组,连 \((LCA(d_{i-1},d_i),d_i)\),\((1,d_1)\)。

建完虚树后再在虚树上做开头的 DP 即可。

于是我们的第一道题就迎刃而解了!

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N=2.5e5+5;
int to[N*2],nx[N*2],st[N],tot,co[N*2];
void add(int u,int v,int w){
	to[++tot]=v,nx[tot]=st[u],st[u]=tot,co[tot]=w;
}
vector<int> edge[N];
int top[N],fa[N],son[N],sz[N],d[2*N],dep[N];
#define ll long long 
ll f[N],g[N];
int lca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
		else y=fa[top[y]];
	}
	if(dep[x]>dep[y])return y;
	return x;
}
int dfn[N],dfn1;
void dfs(int x,int y){
	sz[x]=1,fa[x]=y,dep[x]=dep[y]+1;
	for(int i=st[x];i;i=nx[i]){
		int v=to[i];
		if(v!=y){
			g[v]=min(g[x],(ll)co[i]);
			dfs(v,x),sz[x]+=sz[v];
			if(sz[v]>sz[son[x]])son[x]=v;
		}
	}
}
void dfs2(int x){
	dfn[x]=++dfn1;
	if(son[fa[x]]==x)top[x]=top[fa[x]];
	else top[x]=x;
	if(son[x])dfs2(son[x]);
	for(int i=st[x];i;i=nx[i]){
		int v=to[i];
		if(v!=fa[x]&&v!=son[x])dfs2(v);
	}
}
int K;
int cmp(int x,int y){
	return dfn[x]<dfn[y];
}
int bz[N];
void solve(int x){
	ll sum=0;
	for(int v:edge[x]){
		solve(v);
		sum+=f[v];
	}
	edge[x].clear();
	if(bz[x])f[x]=g[x];
	else f[x]=min(g[x],sum);
	bz[x]=0;
}
int main(){
//	freopen("1.in","r",stdin);
	ios::sync_with_stdio(0);
	cin>>n;
	for(int i=1;i<n;i++){
		int u,v,w;
		cin>>u>>v>>w;
		add(u,v,w),add(v,u,w);
	}
	g[1]=1e18;
	dfs(1,0);
	dfs2(1);
	cin>>m;
	while(m--){
		cin>>K;
		for(int i=1;i<=K;i++){
			cin>>d[i];
			bz[d[i]]=1;
		}
		sort(d+1,d+1+K,cmp);
		int tt=0;
		for(int i=1;i<K;i++){
			d[i+K]=lca(d[i],d[i+1]);
		}
		d[K+K]=1;
		sort(d+1,d+K+K+1,cmp);
		for(int i=1;i<=K+K;i++){
			if(d[i-1]!=d[i])d[++tt]=d[i];
		}
		for(int i=2;i<=tt;i++){
			edge[lca(d[i-1],d[i])].push_back(d[i]);
		}
		solve(1); 
		cout<<f[1]<<"\n";
	}
}

套路

数据范围中看见类似 \(\sum m\le 10^5\) 的东西时就要考虑建虚树了。

但是建虚树人人都会,重点是如何计算。

例题

标签:int,top,笔记,son,学习,LCA,虚树,关键点
From: https://www.cnblogs.com/dccy/p/18434450

相关文章

  • 2-SAT 学习笔记
    2-SAT学习笔记本文同载于本人的洛谷文章。参考资料算法2-SAT用于解决什么样的问题?问题给定\(n\)个大小为2的集合,每个集合要选其中一个元素,不能同时选,有\(m\)个条件\((a,b)\)代表元素\(a,b\)不能同时选,构造方案或判定无解。例子有3个集合:\(\{a,\nega\},\{b,......
  • Python从0到100(五十八):机器学习-随机森林及对复杂数据集分类
    随机森林通过构建多个决策树来完成分类或回归任务。随机森林的核⼼思想是通过多个弱学习器(决策树)的集成来构建⼀个强学习器,从⽽提⾼模型的泛化能⼒和稳定性。1.基本原理随机森林的基本原理如下:从训练集中随机抽取⼀定数量的样本(有放回抽样),构建⼀个决策树(称为⾃助采样法或......
  • 卷积神经网络-迁移学习
    文章目录一、迁移学习1.定义与性质2.步骤二、BatchNormalization(批次归一化)三、ResNet网络1.核心思想2.残差结构(1)残差块(2)残差结构类型四、总结一、迁移学习迁移学习(TransferLearning)是一种强大的机器学习方法,其核心思想是将在一个任务(源任务)上学到的知识或模型......
  • 深度学习:ResNet残差神经网络
    目录一、什么是ResNet残差神经网络二、残差结构三、18层残差网络1.最初残差网络变体2.图片示例3.表格示例四、批次归一化(BatchNormalization)1.工作过程2.主要作用五、ResNet残差神经网络解决了传统神经网络什么问题1.梯度消失和梯度爆炸梯度消失:梯度爆炸:2.退化......
  • 【刷题笔记】2019 CSP-J
    2019CSP-J题目整理B-公交换乘思路梳理先想暴力算法,一遇到公交车,就在已出现过的优惠卷中寻找价格大于等于公交车票价,并且出现时间最早且没有用过的优惠卷,时间复杂度为\(O(n^2)\),必然会炸。但是注意题目中给到的特殊性质,要求如果优惠卷有效,则\[t_{bus}-t_{subway}\le45\]并......
  • 利用大规模无监督学习提升药物分子表示
    人工智能咨询培训老师叶梓转载标明出处在人工智能驱动的药物设计和发现领域,获取具有信息量的分子表示是一个至关重要的前提。近年来,研究者们将分子抽象为图,并利用图神经网络(GNNs)进行分子表示学习,展现出了巨大的潜力。然而,实际应用中GNNs面临着两个主要问题:一是用于监督训练的......
  • JavaWeb基础-学习笔记01
    01JavaWeb介绍一个Web的互联网系统可以分为三个主要部分:网页、JavaWeb程序、数据库网页:展现数据数据库:存储和管理数据javaWeb程序:逻辑处理因此,JavaWeb的学习内容对应以上三部分内容:数据库部分MySQL:一款主流的数据库产品(数据库管理系统),用结构化查询语言SQL操作数据库JD......
  • Java中集合工具类的学习
    集合工具类目录集合工具类Collections类Arrays类Comparator接口总结Java中的集合工具类主要帮助开发者对集合(如List、Set、Map等)进行高效的操作和管理。虽然“三种集合工具类”这一表述可能不完全精确,因为Java集合框架中包含了多个工具类和接口,但我可以根据常见的和重要的工具......
  • repo 简单搭建学习记录
    repo简单搭建学习记录一、repo搭建参考:repo仓库搭建教程【CSDN】gitrepo工具详细使用教程【CSDN】搭建Repo服务器【CSDN】使用REPO管理GIT多仓库1、服务端repo需要一个服务端(manifest仓库),用来列出所有子仓库的路径等信息,如果在github等远程托管平台创建服务端,那......
  • 学习技巧: word文档中写论文会需要的技巧
    之前经常给大家分享办公技巧,今天想给大学生朋友分享一些写论文时候会用到的技巧。技巧一:图片、表格编号及引用论文中少不了图片、表格,而且还需要进行编号,如果我们纯靠自己手动输入,我们需要调节位置还有字体大小什么的,但是我们可以自动编号。方法如下:首先我们先插入图片,右键......