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算法进行的过程中,我们把最小生成树的边权改为点权。 这样,原树 kruskal重构树:简单来讲,就是在kruskal算法进行的过程中,我们把最小生成树的边权改为点权。这样,原树 的 节 点 个 数 变 成 2 n − 1 个 , 并 且 有 着 许 多 有 趣 的 性 质 。 的节点个数变成2n-1个,并且有着许多有趣的性质。 的节点个数变成2n−1个,并且有着许多有趣的性质。
k
r
u
s
k
a
l
重
构
树
的
性
质
:
kruskal重构树的性质:
kruskal重构树的性质:
1
、
根
据
我
们
构
造
的
过
程
,
这
是
一
个
二
叉
堆
1、根据我们构造的过程,这是一个二叉堆
1、根据我们构造的过程,这是一个二叉堆
2
、
原
树
两
点
之
间
的
边
权
最
大
值
是
重
构
树
上
两
点
l
c
a
的
权
值
2、原树两点之间的边权最大值是重构树上两点lca的权值
2、原树两点之间的边权最大值是重构树上两点lca的权值
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倍增数组(根据重构树的性质我们可以发现, 首先我们可以先求出所给图的kruskal重构树,然后dfs求出树的lca倍增数组(根据重构树的性质我们可以发现, k r u s k a l 重 构 树 和 l c a 基 本 上 都 是 一 起 用 的 ) 。 此 外 , 在 d f s 的 过 程 中 , 我 们 让 n w [ u ] = 以 u 为 根 的 子 树 中 所 有 点 的 kruskal重构树和lca基本上都是一起用的)。此外,在dfs的过程中,我们让nw[u]=以u为根的子树中所有点的 kruskal重构树和lca基本上都是一起用的)。此外,在dfs的过程中,我们让nw[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()
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重构树倍增)