前言

没做出来,看了很多篇题解后AC了,感觉大部分题解讲得不清楚。

题目

思路

结果有两种求法

  1. 模拟跑步过程,统计每个节点能观察到的人数
  2. 考虑每条路径会对哪些节点作出贡献(当前路径的玩家能被观察到)

尝试

第一种求法必须遍历当前路径的所有节点,不能优化,因为不能跳过任何节点,最坏时间复杂度O(nm),超时,换思路。

正解思路

不再遍历统计,换第二种求法。

路径 si → ti 若能对节点 u 有贡献,节点u一定是路径L上的节点。

分情况讨论

  1. 节点u在路径 si → lca(si, ti) 上,设depth[i] = 节点i的深度(根节点为1),此时depth[si] = depth[u] + w[u]。
  2. 节点u在路径 lca(si, ti) → ti 上,设dis(x, y) = x到y之间的距离,此时 dis(si, ti) = w[u] + (depth[ti] - depth[u]),移项得 dis(si, ti) - depth[ti] = w[u] - depth[u],移项后等式左边是已知量。

根据等式,且n < 10^6,用桶快速得出节点观察到的人数,设 cntStart[depth[u] + w[u]] = 满足节点 u 的 si(即depth[si] = depth[u] + w[u]),起点为 si 的路径个数;cntLast[w[u] - depth[u]] = 满足节点 u 的 ti,(即dis(si, ti) - depth[ti] = w[u] - depth[u]),终点为ti的路径个数。

设result[u] = 在节点u能观察到的人数(相当于路径数)。

除了节点 u = lca(si, ti) 的情况,节点u要么在路径 si → lca(si, ti) 上,此时 result[u] += 起点为 si 的路径个数,要么在路径 lca(si, ti) → ti 上,此时 result[u] += 终点为ti的路径个数,路径个数正确统计。

当节点 u = lca(si, ti),若 si 和 ti 会对节点u产生贡献,此时 result[u] += 起点为 si 的路径个数 + 终点为ti的路径个数,si → ti 的路径会被计算两次(si 计算一次,ti 计算一次),需要result[u] = result[u] - 1。

当路径 si → ti 的 si 满足 depth[si] = depth[lca(si, ti)] + w[lca(si, ti)],ti也会满足dis(si, ti) - depth[ti] = w[lca(si, ti)] - depth[lca(si, ti)]。

证明

depth[lca(si, ti)] + w[lca(si, ti)] = w[lca(si, ti)] - depth[lca(si, ti)] + 2 × depth[lca(si, ti)],

那么有

dis(si, ti) - depth[ti] + 2 × depth[lca(si, ti)] = w[lca(si, ti)] - depth[lca(si, ti)] + 2 × depth[lca(si, ti)],化简得

depth[si] = depth[lca(si, ti)] + w[lca(si, ti)] 。

证毕

所以只要判断到 depth[si] = depth[lca(si, ti)] + w[lca(si, ti)] ,就result[lca(si, ti)] = result[lca(si, ti)] - 1。

实现

发现无论什么情况,si、ti 都在以节点u为根的子树上,所以 dfs 遍历,回溯的时候统计。

tot1 = 以节点u为根的子树之外的起点 si 满足 depth[si] = depth[u] + w[u] 的路径个数,tot2 = 以节点u为根的子树之外的终点 ti 满足 dis(si, ti) - depth[ti] = w[u] - depth[u] 的路径个数,计算tot1,tot2是为了去掉节点u不再上面的路径数。根据 tot1 和 tot2 的定义,要在下一层递归前求解。

先统计节点u为起点和为终点时有贡献的路径数,可能满足 depth[si] = depth[u] + w[u] 的 si 为 u,满足 dis(si, ti) - depth[ti] = w[u] - depth[u] 的 ti 为 u。

最后减去 lca(si, ti) = u 的路径数,这些路径之后不会再被计入。

LCA用倍增加速求解。

细节见代码。

代码

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxn = 299998 + 5;
const LL Maxm = 299998 + 5;
const LL Maxk = 20;
const LL Size = 300000;struct node {LL s, t, e;
} pla[Maxm];vector<LL> tree[Maxn];
vector<LL> tId[Maxn];
vector<LL> LId[Maxn];
LL vct_w[Maxn], depth[Maxn], father[Maxn][Maxk];
LL cnt_s[Maxn], cntStart[Maxn * 2], cntLast[Maxn * 2], result[Maxn];void init(LL u, LL fa) {depth[u] = depth[fa] + 1;father[u][0] = fa;for (LL i = 1; depth[u] > (1 << i); ++i) {father[u][i] = father[father[u][i - 1]][i - 1];}for (auto v : tree[u]) {if (v != fa)  init(v, u);}return;
}LL LCA(LL u, LL v) {if (depth[u] < depth[v])  swap(u, v);for (LL i = Maxk - 1; i >= 0; --i) {if (depth[father[u][i]] >= depth[v])  u = father[u][i];}if (u == v)  return u;for (LL i = Maxk - 1; i >= 0; --i) {if (father[u][i] != father[v][i]) {u = father[u][i];v = father[v][i];}}return father[u][0];
}void dfs(LL u, LL fa) {LL tot1 = cntStart[depth[u] + vct_w[u]], tot2 = cntLast[vct_w[u] - depth[u] + Size];for (auto v : tree[u]) {if (v != fa)  dfs(v, u);}cntStart[depth[u]] += cnt_s[u];for (auto id : tId[u]) {++cntLast[pla[id].e - depth[pla[id].t] + Size];}result[u] += (cntStart[depth[u] + vct_w[u]] - tot1) + (cntLast[vct_w[u] - depth[u] + Size] - tot2);for (auto id : LId[u]) {--cntStart[depth[pla[id].s]];--cntLast[pla[id].e - depth[pla[id].t] + Size];}return;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL n, m, u, v;cin >> n >> m;for (LL i = 1; i < n; ++i) {cin >> u >> v;tree[u].emplace_back(v);tree[v].emplace_back(u);}for (LL i = 1; i <= n; ++i)  cin >> vct_w[i];init(1, 0);LL lcaId = 0;for (LL i = 1; i <= m; ++i) {cin >> pla[i].s >> pla[i].t;lcaId = LCA(pla[i].s, pla[i].t);pla[i].e = depth[pla[i].s] + depth[pla[i].t] - depth[lcaId] * 2;++cnt_s[pla[i].s];tId[pla[i].t].emplace_back(i);LId[lcaId].emplace_back(i);if (depth[pla[i].s] == depth[lcaId] + vct_w[lcaId])  --result[lcaId];}dfs(1, 0);for (LL i = 1; i <= n; ++i)  cout << result[i] << ' ';return 0;
}

注意

  • cntStart要开到2 * Maxn
  • 明确每个数组的下标和元素是什么,搞错很难调
  • 思路是一条条的路径,被卡住就换条路径。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/89782.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89782.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/89782.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

valkey之网络管理架构深度解析

一、连接类型实现体系 valkey通过ConnectionType结构体构建了灵活的网络连接抽象&#xff0c;支持多种连接类型的统一管理。每种连接类型都通过填充该结构体的函数指针来实现特定功能&#xff0c;形成了面向接口的设计模式。1.1 socket连接 Socket连接提供了最基础的TCP/IP通信…

【解码文本世界的“隐形分界线”:Windows与Linux回车换行之谜】

在计算机的文本世界里&#xff0c;回车&#xff08;Carriage Return&#xff0c;CR&#xff09;和换行&#xff08;Line Feed&#xff0c;LF&#xff09;是两个看似简单却意义非凡的字符。它们如同文本中的“隐形分界线”&#xff0c;默默地划分着段落与行&#xff0c;影响着文…

【Project】ELK 7.17.16 日志分析系统部署

ELK 日志分析系统集群部署 本文档基于 Rocky Linux 9.4 系统&#xff0c;部署 ELK 7.17.16&#xff08;长期支持版&#xff09;集群 案例准备 1. 节点规划IP主机名部署组件角色说明192.168.100.150kafka01Elasticsearch、Kibana主节点&#xff08;master&#xff09; 可视化192…

分布式定时任务系列13:死循环是任务触发的银弹?

传送门 分布式定时任务系列1&#xff1a;XXL-job安装 分布式定时任务系列2&#xff1a;XXL-job使用 分布式定时任务系列3&#xff1a;任务执行引擎设计 分布式定时任务系列4&#xff1a;任务执行引擎设计续 分布式定时任务系列5&#xff1a;XXL-job中blockingQueue的应用 …

Flutter基础(前端教程①③-单例)

现实类比&#xff1a;公司打印机假设你们公司有一台共享打印机&#xff1a;非单例&#xff08;重复创建&#xff09;&#xff1a;每个员工都自己买一台打印机放在工位上结果&#xff1a;浪费钱&#xff0c;占空间&#xff0c;难维护单例&#xff08;唯一实例&#xff09;&#…

力扣刷题 -- 965.单值二叉树

题目示例&#xff1a; 思路分析代码实现 bool isUnivalTree(struct TreeNode* root) {if(rootNULL){return true;}if(root->left && root->val ! root->left->val){return false;}if(root->right && root->val ! root->right->val){re…

uni-api交互反馈组件(showToast)的用法

欢迎来到我的UniApp技术专栏&#xff01;&#x1f389; 在这里&#xff0c;我将与大家分享关于UniApp开发的实用技巧、最佳实践和项目经验。 专栏特色&#xff1a; &#x1f4f1; 跨平台开发一站式解决方案 &#x1f680; 从入门到精通的完整学习路径 &#x1f4a1; 实战项目经…

借助它,在Web3投资赛道抢占先机

随着互联网技术的飞速发展&#xff0c;Web3的概念逐渐成为科技圈和投资界的热门话题。Web3代表着下一代互联网的发展方向&#xff0c;它强调去中心化、用户主权和数据隐私保护。在这一新兴领域&#xff0c;如何借助Web3技术抢占投资先机&#xff0c;成为许多投资者关注的焦点。…

验证大语言模型不会算数但可以编写算数的程序

摘要&#xff1a;本文通过几个实例测试了大语言模型在数学计算、排序、统计等方面的能力。结果显示&#xff0c;对于简单字符统计、排序等任务&#xff0c;大模型能正确生成实现代码&#xff0c;但当数据区分度降低时容易出错。在计算学生分数排名任务中&#xff0c;大模型生成…

概率论与数理统计(八)

参数估计 通过取样本&#xff0c;并用样本构造函数&#xff0c;达成估计分布函数参数的目的 矩估计法 本质&#xff1a;用样本的各阶矩代替总体的各阶矩&#xff0c;即取&#xff1a; E(X)X‾1n∑iXiE(X2)1n∑iXi2E(X)\overline{X}\dfrac{1}{n}\sum_i X_i\\ E(X^2)\dfrac{1}…

服务器后台崩溃的原因

当我们双十一活动零点拼命刷新却卡在支付完页面&#xff0c;游戏页面等不进去&#xff0c;公司系统瘫痪全体员工干瞪眼&#xff0c;服务器崩溃绝对是数字时代中的酷刑&#xff01;那服务器为什么会说崩就崩&#xff0c;用户对于这种情况该如何进行避雷呢&#xff1f;服务器主要…

线程池与ThreadPoolExecutor源码解析(上)

一、线程池线程池&#xff08;ThreadPool&#xff09;是一种线程复用的机制。它维护着若干个线程&#xff0c;任务来了就复用这些线程去执行&#xff0c;任务做完线程不会销毁&#xff0c;而是回到池中等待下一个任务。为什么要用线程池&#xff1f;降低资源消耗&#xff1a;避…

Linux内核IP分片重组机制剖析:高效与安全的艺术

在IP网络通信中,当数据包超过MTU限制时,路由器会将其拆分为多个分片。这些分片到达目标主机后,内核必须高效、安全地重组原始数据包。Linux内核的net/ipv4/inet_fragment.c实现了一套精妙的分片管理框架,完美平衡了性能和安全性需求。本文将深入剖析其设计哲学与关键技术。…

相机模型和对极几何

一、相机模型 1.针孔相机模型-外参矩阵 1.世界坐标系到相机坐标系 世界坐标系&#xff1a;可以定义空间中任意一个位置&#xff0c;原点位置三个坐标轴方向坐标系姿态&#xff08;X,Y,Z&#xff09;相机坐标系&#xff1a;定义在相机上&#xff0c;原点是相机中心&#xff0c;z…

Git 常用命令与操作步骤

以下是 Git 常用命令与操作步骤 的整理&#xff0c;涵盖日常开发中最核心的场景&#xff0c;适合快速查阅和上手&#xff1a;1. 初始化与克隆仓库操作命令本地初始化仓库git init克隆远程仓库git clone <仓库URL> &#xff08;如 git clone https://gitlab.com/user/repo…

Leetcode-.283移动零

class Solution:def moveZeroes(self, nums: List[int]) -> None:"""Do not return anything, modify nums in-place instead."""pos0for i in range(len(nums)):if nums[i]!0:nums[pos],nums[i]nums[i],nums[pos]pos1本题运用双指针来写&…

在React中做过哪些性能优化?

1. 使用 React.memo 进行组件优化 问题:当父组件重新渲染时,子组件也会重新渲染,即使它的 props 没有变化。 解决方案:使用 React.memo 包裹子组件,让其只在 props 变化时才重新渲染。 const MyComponent = React.memo((props) => {// 子组件代码 }); 2. 使用 useCa…

安装docker可视化工具 Portainer中文版(ubuntu上演示,所有docker通用) 支持控制各种容器,容器操作简单化 降低容器门槛

以下有免费的4090云主机提供ubuntu22.04系统的其他入门实践操作 地址&#xff1a;星宇科技 | GPU服务器 高性能云主机 云服务器-登录 相关兑换码星宇社区---4090算力卡免费体验、共享开发社区-CSDN博客 兑换码要是过期了&#xff0c;可以私信我获取最新兑换码&#xff01;&a…

ansible批量部署zabbix客户端

✅ansible编写剧本步骤 1️⃣创建roles目录结构2️⃣在group_vars/all/main.yml中定义变量列表3️⃣在tasks目录下编写tasks任务4️⃣在files目录下准备部署文件5️⃣在templates目录下创建j2模板文件6️⃣在handlers目录下编写handlers7️⃣在roles目录下编写主playbook8️⃣运…

蚂蚁数科AI数据产业基地正式投产,携手苏州推进AI产业落地

近日&#xff0c;蚂蚁数科AI数据产业基地在太仓智汇谷科技创新园正式投产。该基地作为苏州市首个AI数据产业基地&#xff0c;旨在通过跨行业人才与前沿技术&#xff0c;为长三角制造业、金融、医疗等领域的大模型落地提供场景化、高质量的训练数据支撑。数据被视为AI学习的核心…