题解:Luogu-P9433 [NAPC-#1] Stage5 - Conveyors
个人认为,做这种题把思路想清晰代码自然就写出来了,所以要注重思考的过程,重点把握一些自己想不到的性质或技巧。
本篇题解在现有题解的基础上对解法进行了更详细的说明。阅读前建议确保自己会树链剖分和树剖求最近公共祖先。
解题报告
约定:\(dis_{a,b}\) 表示 \(a\) 到 \(b\) 的简单路径,\(\text{LCA}(a,b)\) 表示 \(a,b\) 的最近公共祖先。
由于需要把所有关键节点都走一遍,所以我们不可避免地需要把包含所有关键节点的最小连通块求出来,同时计算该连通块内的边权和 \(sum\)。
接下来我们观察询问的 \(s,t\) 节点,猜想答案与 \(s,t\) 的位置相关,于是我们进行分类讨论。
- 当 \(s,t\) 均在包含所有关键节点的最小连通块上时,答案即为 \(2\times sum-dis_{s,t}\)。因为此时的最短路径为 \(s\) 到 \(t\) 的简单路径走一遍,然后连通块其他边走两遍。该情况对应样例中的第 \(1,2\) 次询问。
- 当 \(s\) 和 \(t\) 中有一个不在该最小连通块上时,假设 \(s\) 不在,求出 \(s\) 最靠近的连通块上的点 \(x\),则由第一种情况容易得出答案为 \(dis_{s,x}+2\times sum-dis_{x,t}\)。
- 当 \(s\) 和 \(t\) 都不在该最小连通块上时,求出 \(s\) 最靠近的连通块上的点 \(x\) 和 \(t\) 最靠近的连通块上的点 \(y\),由第二种情况容易推出答案为 \(dis_{s,x}+2\times sum-dis_{x,y}+dis_{y,t}\)。
综上所述,求答案的式子就是 \(dis_{s,x}+2\times sum-dis_{x,y}+dis_{y,t}\)。接下来考虑如何实现。
主要任务之一是快速求 \(dis_{a,b}\)。我们令 \(dis(x)\) 表示第一个关键节点到 \(x\) 的最短距离,则 \(dis_{a,b}=dis(a)+dis(b)-2\times dis(\text{LCA}(a,b))\)。
另外一个主要任务就是求连通块以外的点最靠近的连通块上的点,这使用一次 DFS,从连通块上的点向外拓展即可。
参考代码
#include <iostream>
#define reg register
#define IL inline
using namespace std;
const int N = 1e5 + 10;
int n, q, k, key[N];
bool IMP[N];
int head[N], cnt;
struct EDGE{int to, val, nxt; } edge[N << 1];
IL void addedge(reg int u, reg int v, reg int w) {
edge[++cnt].to = v, edge[cnt].val = w, edge[cnt].nxt = head[u];
head[u] = cnt; return;
}//链式前向星存树
int fa[N], dep[N], sz[N], son[N], top[N];
//基本信息:祖先fa,深度dep,子树大小sz,重儿子son,链头top
int dis[N], tonear[N], ct[N], near[N], sum;
IL void dfs1(reg int u, reg int p) {
fa[u] = p, sz[u] = 1, dep[u] = dep[p] + 1;
for(reg int i = head[u], v, w; i; i = edge[i].nxt) {
v = edge[i].to, w = edge[i].val;
if(v == p) continue;
dis[v] = dis[u] + w; //求dis(x)
dfs1(v, u);
ct[u] += ct[v];
if(ct[v] && k - ct[v] > 0) IMP[u] = 1; //判断是否在最小连通块上
sz[u] += sz[v];
if(!son[u] || sz[son[u]] < sz[v]) son[u] = v;
}
return;
}
IL void dfs2(reg int u, reg int topu) {
top[u] = topu;
if(!son[u]) return;
dfs2(son[u], topu);
for(reg int i = head[u], v; i; i = edge[i].nxt) {
v = edge[i].to;
if(v != fa[u] && v != son[u]) dfs2(v, v);
}
return;
} //树剖基本操作
IL int lca(reg int x, reg int y) {
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]]; //跳链
}
if(dep[x] > dep[y]) swap(x, y);
return x;
} //树剖lca
IL void dfs(reg int u, reg int p, reg int now, reg int dist) {
near[u] = now, tonear[u] = dist; //求最近点near以及u到最近点的距离
for(reg int i = head[u], v, w; i; i = edge[i].nxt) {
v = edge[i].to, w = edge[i].val;
if(v == p) continue;
if(IMP[v]) sum += w, dfs(v, u, v, dist);
else dfs(v, u, now, dist + w);
}
return;
}
IL int calc(reg int u, reg int v) {
reg int tmp = dis[near[u]] + dis[near[v]] - (dis[lca(near[u], near[v])] << 1);
return tonear[u] + (sum << 1) - tmp + tonear[v]; //计算
}
int main(){
scanf("%d %d %d", &n, &q, &k);
for(reg int i = 1, u, v, w; i < n; i++) {
scanf("%d %d %d", &u, &v, &w); addedge(u, v, w), addedge(v, u, w);
}
for(reg int i = 1; i <= k; i++) {
scanf("%d", &key[i]); IMP[key[i]] = true;
ct[key[i]] = 1;
}
dfs1(1, 1), dfs2(1, 1); dfs(key[1], 0, key[1], 0);
while(q--) {
reg int u, v; scanf("%d %d", &u, &v);
printf("%d\n", calc(u, v));
}
return 0;
}
如有不足,还请指出,谢谢。
题解:Luogu-P9433 [NAPC-#1] Stage5 - Conveyors的更多相关文章
- [题解] Luogu P5446 [THUPC2018]绿绿和串串
[题解] Luogu P5446 [THUPC2018]绿绿和串串 ·题目大意 定义一个翻转操作\(f(S_n)\),表示对于一个字符串\(S_n\), 有\(f(S)= \{S_1,S_2,..., ...
- 题解 Luogu P2499: [SDOI2012]象棋
关于这道题, 我们可以发现移动顺序不会改变答案, 具体来说, 我们有以下引理成立: 对于一个移动过程中的任意一个移动, 若其到达的位置上有一个棋子, 则该方案要么不能将所有棋子移动到最终位置, 要么可 ...
- 题解 luogu P1144 【最短路计数】
本蒟蒻也来发一次题解第一篇请见谅 这个题有几个要点 1.无向无权图,建图的时候别忘记建来回的有向边[因此WA掉1次 2.无权嘛,那么边长建成1就好了2333333 3.最短路采用迪杰斯特拉(别忘用堆优 ...
- 题解 Luogu P1110 【[ZJOI2007]报表统计】
感谢 @cmy962085349 提供的hack数据,已经改对了. 先声明,我好像是题解里写双$fhq$ $treap$里唯一能过的...(最后两个点啊) 思路:首先看题目,$MIN_GAP_SORT ...
- 题解 Luogu P3370
讲讲这题的几种做法: 暴力匹配法 rt,暴力匹配,即把字符串存起来一位一位判相等 时间复杂度$ O(n^2·m) $ 再看看数据范围 \(n\le10^5,m\le10^3\) 当场爆炸.当然有暴力分 ...
- 题解 Luogu P3623 [APIO2008]免费道路
[APIO2008]免费道路 题目描述 新亚(New Asia)王国有 N 个村庄,由 M 条道路连接.其中一些道路是鹅卵石路,而其它道路是水泥路.保持道路免费运行需要一大笔费用,并且看上去 王国不可 ...
- [题解]luogu P4116 Qtree3
终于来到了Qtree3, 其实这是Qtree系列中最简单的一道题,并不需要线段树, 只要树链剖分的一点思想就吼了. 对于树链剖分剖出来的每一根重链,在重链上维护一个Set就好了, 每一个Set里存的都 ...
- 题解 Luogu P3959 【宝藏】
来一篇不那么慢的状压??? 话说这题根本没有紫题难度吧,数据还那么水 我是不会告诉你我被hack了 一看数据规模,n≤12,果断状压. 然后起点要枚举,就设dp状态: f[i][j]=以i为起点到j状 ...
- 题解 Luogu P1099 【树网的核】
这题是真的水啊... ------------ 昨天模拟赛考了这题,很多人都是O($n^3$)水过,但我认为,要做就做的足够好(其实是我根本没想到O($n^3$)的做法),然后就开始想O(n)的解法. ...
- HDU 4549题解 & luogu【模板】矩阵加速(数列)
M斐波那契数列 此题对数学基础要求较高 来源矩阵乘法_百度百科 一个m*n的矩阵是一个由m行n列元素排成的矩形阵列.矩阵里的元素可以是数字符号或者数学式. 形如[acbd][abcd]的数表称为二阶矩 ...
随机推荐
- HTML5和CSS3基础
HTML元素 空元素 不是所有元素都拥有开始标签.内容和结束标签.一些元素只有一个标签,通常用来在此元素所在位置插入/嵌入一些东西.这些元素被称为空元素例如:元素 `` 是用来在页面插入一张指定的图片 ...
- Linux系统搭建单机MySQL8.0.26版本
概述 本文主要是写Ubuntu22.04搭建MySQL8.0.26版本 环境信息 IP 系统 规格 10.0.0.10 Ubuntu22.04 2c4g 数据库服务安装步骤 下载前置依赖 # 下载li ...
- 『Plotly实战指南』--在金融数据可视化中的应用(上)
在当今复杂多变的金融市场中,金融数据分析的重要性不言而喻. 无论是投资者.金融机构还是研究人员,都需要通过对海量金融数据的分析来洞察市场趋势.评估风险并做出明智的决策. 据彭博社统计,专业投资者平均需 ...
- Oracle ACL (Access Control List) 详细介绍
参考:https://blog.csdn.net/qq243348167/article/details/87876956 --查询acl信息 SELECT * FROM dba_network_ac ...
- springboot加载配置文件的优先级
如果springboot有多个配置文件,则加载顺序为项目根路径下的/config > 项目根路径 > resources/config > resources目录下 图示: 如果两个 ...
- DataFrame.iterrows的一种用法
import pandas as pd import numpy as np help(pd.DataFrame.iterrows) Help on function iterrows in modu ...
- 取余(rem)和取模(mod)的区别
设 A rem B || A mod B 生成机制 取余:采取fix()函数,向0方向取整 取模:采取floor()函数,向无穷小方向取整 当A,B异号时(其实同号也是这个规律-) 取余:结果和A同号 ...
- php伪随机数爆破
php伪随机数爆破 涉及到的函数为mt_rand() mt_rand(min, max) 返回min到max之间的伪随机数,如果参数缺省,则返回0到RAND_MAX之间的伪随机数. 不同于常规的伪随机 ...
- 第1讲、#PyTorch教学环境搭建与Tensor基础操作详解
引言 PyTorch是当前深度学习领域最流行的框架之一,因其动态计算图和直观的API而备受开发者青睐.本文将从零开始介绍PyTorch的环境搭建与基础操作,适合各种平台的用户和深度学习初学者. 1. ...
- Spring Boot注解之@ComponentScan用法和实现原理
注解@ComponentScan的作用 @Component注解及其衍生注解@RestController.@Controller.@Service和@Repository都是组件注册注解.@Co ...