当前位置: 首页 > news >正文

题解:洛谷 P1600 [NOIP 2016 提高组] 天天爱跑步

【题目来源】

洛谷:[P1600 NOIP 2016 提高组] 天天爱跑步 - 洛谷

【题目描述】

小 C 同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

这个游戏的地图可以看作一棵包含 \(n\) 个结点和 \(n-1\) 条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 \(1\)\(n\) 的连续正整数。

现在有 \(m\) 个玩家,第 \(i\) 个玩家的起点为 \(s_i\),终点为 \(t_i\)。每天打卡任务开始时,所有玩家在第 \(0\) 秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去,跑到终点后该玩家就算完成了打卡任务。(由于地图是一棵树,所以每个人的路径是唯一的)

小 C 想知道游戏的活跃度,所以在每个结点上都放置了一个观察员。在结点 \(j\) 的观察员会选择在第 \(w_j\) 秒观察玩家,一个玩家能被这个观察员观察到当且仅当该玩家在第 \(w_j\) 秒也正好到达了结点 \(j\)。小 C 想知道每个观察员会观察到多少人?

注意:我们认为一个玩家到达自己的终点后该玩家就会结束游戏,他不能等待一 段时间后再被观察员观察到。 即对于把结点 \(j\) 作为终点的玩家:若他在第 \(w_j\) 秒前到达终点,则在结点 \(j\) 的观察员不能观察到该玩家;若他正好在第 \(w_j\) 秒到达终点,则在结点 \(j\) 的观察员可以观察到这个玩家。

【输入】

第一行有两个整数 \(n\)\(m\)。其中 \(n\) 代表树的结点数量, 同时也是观察员的数量, \(m\) 代表玩家的数量。

接下来 \(n-1\) 行每行两个整数 \(u\)\(v\),表示结点 \(u\) 到结点 \(v\) 有一条边。

接下来一行 \(n\) 个整数,其中第 \(j\) 个整数为 \(w_j\) , 表示结点 \(j\) 出现观察员的时间。

接下来 \(m\) 行,每行两个整数 \(s_i\),和 \(t_i\),表示一个玩家的起点和终点。

对于所有的数据,保证 \(1\leq s_i,t_i\leq n, 0\leq w_j\leq n\)

【输出】

输出 \(1\)\(n\) 个整数,第 \(j\) 个整数表示结点 \(j\) 的观察员可以观察到多少人。

【输入样例】

6 3
2 3
1 2 
1 4 
4 5 
4 6 
0 2 5 1 2 3 
1 5 
1 3 
2 6 

【输出样例】

2 0 0 1 1 1 

【算法标签】

《洛谷 P1600 天天爱跑步》 #线段树# #树上起发式合并# #最近公共祖先LCA# #树链剖分# #动态树LCT# #ST表# #差分# #NOIP提高组# #2016#

【代码详解】

#include <bits/stdc++.h>
using namespace std;const int N = 300005;  // 定义最大节点数int n, m;              // n: 节点数, m: 路径数
int deep[N];           // 存储每个节点的深度
int f[N][25];          // 倍增数组,f[i][j]表示节点i的2^j级祖先
vector<int> ve[N];     // 邻接表存储树结构
int w[N];              // 每个节点的观察值
int u, v, s, t, l;     // 临时变量// 定义路径操作结构体
struct Node
{int t;  // 操作类型(0: 上行路径, 1: 下行路径)int v;  // 路径参数值
};vector<Node> add[N];   // 在节点处添加的路径操作
vector<Node> del[N];   // 在节点处删除的路径操作
int cnt[2][N * 2];     // 计数器数组,[0]用于上行路径,[1]用于下行路径
int ans[N];            // 存储每个节点的答案/*** 第一次DFS:计算深度和父节点信息* @param u 当前节点* @param fa 父节点*/
void dfs_1(int u, int fa)
{deep[u] = deep[fa] + 1;  // 计算当前节点深度f[u][0] = fa;            // 记录直接父节点// 遍历所有子节点for (int i = 0; i < ve[u].size(); i++){int tmp_v = ve[u][i];if (tmp_v == fa) continue;  // 跳过父节点dfs_1(tmp_v, u);            // 递归处理子节点}
}/*** 初始化倍增数组*/
void init()
{for (int i = 1; i <= 20; i++){for (int j = 1; j <= n; j++){f[j][i] = f[f[j][i - 1]][i - 1];  // 计算2^i级祖先}}
}/*** 计算两个节点的最近公共祖先(LCA)* @param x 第一个节点* @param y 第二个节点* @return 最近公共祖先节点*/
int lca(int x, int y)
{// 确保x的深度不小于yif (deep[x] < deep[y]){swap(x, y);}// 将x提升到与y同一深度for (int i = 20; i >= 0; i--){if (deep[f[x][i]] >= deep[y]){x = f[x][i];}}// 如果x和y相同,直接返回if (x == y){return x;}// 同时提升x和y直到它们的父节点相同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];  // 返回最近公共祖先
}/*** 第二次DFS:统计路径信息并计算答案* @param x 当前节点*/
void dfs_2(int x)
{// 记录当前计数器的值(用于差分计算)int pre_1 = cnt[0][w[x] + deep[x] + N];int pre_2 = cnt[1][w[x] - deep[x] + N];// 处理当前节点的添加操作for (int i = 0; i < add[x].size(); i++){Node sn = add[x][i];cnt[sn.t][sn.v + N]++;  // 增加对应计数}// 处理当前节点的删除操作for (int i = 0; i < del[x].size(); i++){Node sn = del[x][i];cnt[sn.t][sn.v + N]--;  // 减少对应计数}// 递归处理所有子节点for (int i = 0; i < ve[x].size(); i++){int tmp_v = ve[x][i];if (tmp_v == f[x][0]) continue;  // 跳过父节点dfs_2(tmp_v);                    // 递归处理子节点}// 计算当前节点的答案(差分计算)ans[x] = cnt[0][w[x] + deep[x] + N] + cnt[1][w[x] - deep[x] + N] - pre_1 - pre_2;return;
}int main()
{// 输入节点数和路径数cin >> n >> m;// 输入树的边for (int i = 1; i < n; i++){cin >> u >> v;ve[u].push_back(v);ve[v].push_back(u);}// 输入每个节点的观察值for (int i = 1; i <= n; i++){cin >> w[i];}// 第一次DFS:预处理深度和父节点信息dfs_1(1, 0);init();  // 初始化倍增数组// 处理每条路径for (int i = 1; i <= m; i++){cin >> s >> t;l = lca(s, t);  // 计算最近公共祖先// 处理上行路径(s到lca)add[s].push_back({0, deep[s]});del[f[l][0]].push_back({0, deep[s]});// 处理下行路径(lca到t)add[t].push_back({1, deep[s] - 2 * deep[l]});del[l].push_back({1, deep[s] - 2 * deep[l]});}// 第二次DFS:计算每个节点的答案dfs_2(1);// 输出所有节点的答案for (int i = 1; i <= n; i++){cout << ans[i] << " ";}cout << endl;return 0;
}

【运行结果】

6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
2 0 0 1 1 1 
http://www.jsqmd.com/news/394615/

相关文章:

  • 美团礼品卡回收实用指南解决闲置困扰 - 京顺回收
  • 题解:洛谷 P2052 [NOI2011] 道路修建
  • 题解:洛谷 P1351 [NOIP 2014 提高组] 联合权值
  • 题解:洛谷 P5836 [USACO19DEC] Milk Visits S
  • YOLO26涨点改进 | 全网独家创新、注意力改进篇 | SCI一区 2025 | YOLO26引入MSCA多尺度稀疏交叉聚合,GCBAM分组注意力,助力遥感目标检测、图像分类任务有效涨点
  • 题解:洛谷 P3128 [USACO15DEC] Max Flow P
  • 题解:洛谷 P3379 【模板】最近公共祖先(LCA)
  • 题解:洛谷 P1395 会议
  • Claude Sonnet 4.6发布,操控计算机能力大幅提升,100万token上下文
  • 京东 e 卡闲置别浪费!可可收更安心,这样选最省心 - 可可收
  • 题解:洛谷 P3372 【模板】线段树 1
  • 研磨废水回用厂家怎么挑?2026年攻略来了,实验室废水处理/研磨废水回用(处理)/零排放清洗,研磨废水回用源头厂家找哪家 - 品牌推荐师
  • 题解:洛谷 P1099 [NOIP 2007 提高组] 树网的核
  • 闲置支付宝立减金别浪费!合规回收方法来了,新手也能轻松上手 - 可可收
  • Python3教程梳理
  • 题解:洛谷 P5908 猫猫和企鹅
  • 题解:洛谷 P5677 [GZOI2017] 配对统计
  • 2026沸石转轮一体机企业TOP榜:哪些品牌值得关注?催化燃烧/旋风除尘器/除尘器,沸石转轮制造厂家排行榜单 - 品牌推荐师
  • 瑞祥商联卡闲置不用?这样的合规回收方式,新手也能轻松上手 - 可可收
  • 2026年值得推荐的AVIF转WebP在线工具盘点(支持批量转换)
  • 微信立减金回收技巧:47%闲置率下,5招盘活你的“隐形财富” - 可可收
  • 2026年溴化锂中央空调选购指南:值得关注的公司,溴化锂冷水机组/二手溴化锂中央空调,溴化锂中央空调制造企业有哪些 - 品牌推荐师
  • PAM4相关概念
  • 2026年行业内评价高的调节阀厂商如何选,电液动盲板阀/蝶式止回阀/微阻缓闭止回阀/伸缩蝶阀,调节阀生产厂家哪家权威 - 品牌推荐师
  • 2026年中考体育训练新趋势:智慧体育制造企业深度解析,智能运动手环管理平台/握力测试仪,智慧体育生产厂家哪家好 - 品牌推荐师
  • 闲置分期乐购物额度用不上?教你安全高效回收,不浪费一分额度 - 可可收
  • 题解:洛谷 P1631 序列合并
  • 题解:洛谷 P4053 [JSOI2007] 建筑抢修
  • 题解:洛谷 P2161 [SHOI2009] 会场预约
  • 书籍-阿尔伯特·赫尔曼《亚洲古代地理学》