2021 ICPC上海 H.Life is a Game(Kruskal重构树)

Posted lwz_159

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021 ICPC上海 H.Life is a Game(Kruskal重构树)相关的知识,希望对你有一定的参考价值。

题目描述

题目链接

题目大意

给你一个图,图上有点权和边权。以及q个查询:每个查询给你一个初始位置x和初始能量k。
你每到一个新点上即可获得该点的能量(即点权),但是如果想通过一条边,你的能量总数需要大于边权。
问:可以获取的最大能量数

题目分析

这 道 题 我 们 需 要 用 k r u s k a l 重 构 树 来 解 决 。 这道题我们需要用kruskal重构树来解决。 kruskal

k r u s k a l 重 构 树 : 简 单 来 讲 , 就 是 在 k r u s k a l 算 法 进 行 的 过 程 中 , 我 们 把 最 小 生 成 树 的 边 权 改 为 点 权 。 这 样 , 原 树 kruskal重构树:简单来讲,就是在kruskal算法进行的过程中,我们把最小生成树的边权改为点权。 这样,原树 kruskalkruskal 的 节 点 个 数 变 成 2 n − 1 个 , 并 且 有 着 许 多 有 趣 的 性 质 。 的节点个数变成2n-1个,并且有着许多有趣的性质。 2n1

k r u s k a l 重 构 树 的 性 质 : kruskal重构树的性质: kruskal
1 、 根 据 我 们 构 造 的 过 程 , 这 是 一 个 二 叉 堆 1、根据我们构造的过程,这是一个二叉堆 1
2 、 原 树 两 点 之 间 的 边 权 最 大 值 是 重 构 树 上 两 点 l c a 的 权 值 2、原树两点之间的边权最大值是重构树上两点lca的权值 2lca
3 、 重 构 树 中 代 表 原 树 中 的 点 的 节 点 全 是 叶 子 节 点 , 其 余 节 点 都 代 表 了 一 条 边 的 边 权 。 3、重构树中代表原树中的点的节点全是叶子节点,其余节点都代表了一条边的边权。 3
( k r u s k a l 重 构 图 的 构 造 方 法 在 代 码 里 ) (kruskal重构图的构造方法在代码里) kruskal

对 k r u s k a l 重 构 树 有 了 基 本 的 了 解 之 后 , 我 们 接 着 往 后 看 。 对kruskal重构树有了基本的了解之后,我们接着往后看。 kruskal

首 先 我 们 可 以 先 求 出 所 给 图 的 k r u s k a l 重 构 树 , 然 后 d f s 求 出 树 的 l c a 倍 增 数 组 ( 根 据 重 构 树 的 性 质 我 们 可 以 发 现 , 首先我们可以先求出所给图的kruskal重构树,然后dfs求出树的lca倍增数组(根据重构树的性质我们可以发现, kruskaldfslca k r u s k a l 重 构 树 和 l c a 基 本 上 都 是 一 起 用 的 ) 。 此 外 , 在 d f s 的 过 程 中 , 我 们 让 n w [ u ] = 以 u 为 根 的 子 树 中 所 有 点 的 kruskal重构树和lca基本上都是一起用的)。此外,在dfs的过程中,我们让nw[u]=以u为根的子树中所有点的 kruskallcadfsnw[u]=u 点 权 和 ( 原 图 的 点 ) 点权和(原图的点)

做 完 前 面 的 准 备 之 后 , 我 们 来 正 式 处 理 每 个 查 询 。 做完前面的准备之后,我们来正式处理每个查询。

对 于 一 个 查 询 : u ( 起 点 ) 和 s ( 初 始 值 ) 。 对于一个查询:u(起点)和s(初始值)。 u()s()
我 们 从 起 点 u 开 始 往 上 跳 , 如 果 该 位 置 的 点 权 ( 树 上 的 点 权 为 这 棵 子 树 内 所 有 实 点 的 边 权 最 大 值 ) 小 于 我 们 当 前 我们从起点u开始往上跳,如果该位置的点权(树上的点权为这棵子树内所有实点的边权最大值)小于我们当前 u 能 量 数 ( 实 点 权 和 + 初 始 值 ) , 就 跳 过 去 , 同 时 更 新 能 量 数 ( 新 树 上 的 实 点 权 和 + 初 始 值 ) 。 再 继 续 看 能 不 能量数(实点权和+初始值),就跳过去,同时更新能量数(新树上的实点权和+初始值)。再继续看能不 ++ 能 往 上 跳 … … 一 直 到 不 能 再 往 上 跳 了 为 止 ( 因 为 是 通 过 倍 增 法 向 上 跳 的 , 所 以 单 次 查 找 的 复 杂 度 只 有 l o g 2 n ) 能往上跳……一直到不能再往上跳了为止(因为是通过倍增法向上跳的,所以单次查找的复杂度只有log_2n) log2n

代码如下

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <algorithm>
#include <iomanip>
#define LL long long
#define ULL unsigned long long
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define PDD pair<double,double>
#define x first
#define y second
using namespace std;
const int N=2e5+5,mod=998244353;
struct Edge				//边集
	int u,v,w;
	bool operator< (const Edge e)const
	 return w<e.w; 
e[N];
vector<int> h[N];
LL nw[N],st[N];			//nw[]记录实体图的点权,st[]记录实体图的边权
int p[N],f[N][20];		//f[][]为倍增法lca的fa数组
int find(int x)			//并查集模板

	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];

int kruskal(int n,int m)		//求kruskal重构树的模板

	int cnt=n;
	sort(e,e+m);				//对边排序
	for(int i=0;i<m;i++)		//枚举边
	
		int u=find(e[i].u),v=find(e[i].v),w=e[i].w;
		if(u!=v)			//如果u-v不连通,则连一条边
		
			st[++cnt]=w;					//新建一个点,点权为u-v边权w
			p[cnt]=p[u]=p[v]=cnt;			//将根节点置为新点(保证所以实点都为叶节点)
			h[u].push_back(cnt);			//连边
			h[v].push_back(cnt);
			h[cnt].push_back(u);
			h[cnt].push_back(v);
		
	
	return cnt;

void dfs(int u,int fa)		//求lca的倍增数组

	f[u][0]=fa;
	for(int i=1;i<20;i++) f[u][i]=f[f[u][i-1]][i-1];
	for(int v:h[u])
	
		if(v==fa) continue;
		dfs(v,u);
		nw[u]+=nw[v];		//nw[u]更新为以u为根的子树内所以实点的点权和
	

int main()

	cin.tie(0以上是关于2021 ICPC上海 H.Life is a Game(Kruskal重构树)的主要内容,如果未能解决你的问题,请参考以下文章

2021 ICPC上海 H.Life is a Game(Kruskal重构树)

2021 ICPC上海 H.Life is a Game(Kruskal重构树)

2021 ICPC上海 H.Life is a Game(Kruskal重构树)

2021ICPC上海 H.Life is a Game (kruskal重构树倍增)

2021上海ICPC--H.Life is a Game--Kruskal重构树--铜牌拦路虎

2021 ICPC上海 G.Edge Groups(树形dp)