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

《P2680 [NOIP 2015 提高组] 运输计划》

题目背景

NOIP2015 Day2T3

题目描述

公元 2044 年,人类进入了宇宙纪元。

L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。

小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui​ 号星球沿最快的宇航路径飞行到 vi​ 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj​,并且任意两艘飞船之间不会产生任何干扰。

为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。

如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?

输入格式

第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。

接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai​,bi​ 和 ti​,表示第 i 条双向航道修建在 ai​ 与 bi​ 两个星球之间,任意飞船驶过它所花费的时间为 ti​。

接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj​ 和 vj​,表示第 j 个运输计划是从 uj​ 号星球飞往 vj​号星球。

输出格式

一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。

输入输出样例

输入 #1复制

6 3 
1 2 3 
1 6 4 
3 1 7 
4 3 6 
3 5 5 
3 6 
2 5 
4 5

输出 #1复制

11

说明/提示

所有测试数据的范围和特点如下表所示

请注意常数因子带来的程序效率上的影响。

对于 100% 的数据,保证:1≤ai​,bi​≤n,0≤ti​≤1000,1≤ui​,vi​≤n。

代码实现:

#include<bits/stdc++.h>
#define int long long
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)

const int MAXN = 300005;

int planetCount, planCount, maxPlanLength;

struct Edge {
int to;
int weight;
};

vector<Edge> adj[MAXN];  // 邻接表存储树结构
int ancestor[MAXN][30];  // 倍增法LCA祖先数组
int logTable[MAXN];      // logTable[i] = log2(i)
int nodeDepth[MAXN];     // 节点深度
int nodeDistance[MAXN];  // 根到节点的距离
int diffCount[MAXN];     // 差分数组
int planLCA[MAXN];       // 每个运输计划的LCA
int planDistance[MAXN];  // 每个运输计划的路径长度

struct TransportPlan {
int start;
int end;
};

TransportPlan plans[MAXN];  // 存储所有运输计划

// 预处理DFS:计算深度、距离和直接父节点
void preprocessDFS(int currentNode, int parentNode) {
nodeDepth[currentNode] = nodeDepth[parentNode] + 1;
ancestor[currentNode][0] = parentNode;

for (Edge edge : adj[currentNode]) {
int neighborNode = edge.to;
if (neighborNode == parentNode) continue;
nodeDistance[neighborNode] = nodeDistance[currentNode] + edge.weight;
preprocessDFS(neighborNode, currentNode);
}
}

// 计算两个节点的最近公共祖先
int findLCA(int nodeA, int nodeB) {
if (nodeDepth[nodeA] < nodeDepth[nodeB]) swap(nodeA, nodeB);

// 将nodeA提升到与nodeB相同深度
while (nodeDepth[nodeA] > nodeDepth[nodeB]) {
nodeA = ancestor[nodeA][logTable[nodeDepth[nodeA] - nodeDepth[nodeB]]];
}

if (nodeA == nodeB) return nodeA;

// 同时向上跳跃,找到LCA
for (int i = logTable[nodeDepth[nodeA]]; i >= 0; i--) {
if (ancestor[nodeA][i] != ancestor[nodeB][i]) {
nodeA = ancestor[nodeA][i];
nodeB = ancestor[nodeB][i];
}
}

return ancestor[nodeA][0];
}

// 树上差分还原:计算每条边被覆盖的次数
void dfsForDiff(int currentNode, int parentNode) {
for (Edge edge : adj[currentNode]) {
int neighborNode = edge.to;
if (neighborNode == parentNode) continue;
dfsForDiff(neighborNode, currentNode);
diffCount[currentNode] += diffCount[neighborNode];
}
}

// 二分法检查是否存在可行的改造方案
bool checkFeasible(int candidateTime) {
memset(diffCount, 0, sizeof(diffCount));
int countExceed = 0;
int maxExceedLength = 0;

// 找出所有超过候选时间的路径,进行差分标记
for (int i = 1; i <= planCount; i++) {
if (planDistance[i] > candidateTime) {
diffCount[plans[i].start]++;
diffCount[plans[i].end]++;
diffCount[planLCA[i]] -= 2;
countExceed++;
}
maxExceedLength = max(maxExceedLength, planDistance[i]);
}

// 如果没有超过的路径,直接返回可行
if (countExceed == 0) return true;

// 还原差分数组,计算每条边的覆盖次数
dfsForDiff(1, 0);

// 检查是否存在一条边,满足所有超过的路径都经过它,并且改造后总时间不超过候选时间
for (int i = 2; i <= planetCount; i++) {
if (diffCount[i] == countExceed) {
int edgeWeight = nodeDistance[i] - nodeDistance[ancestor[i][0]];
if (maxExceedLength - edgeWeight <= candidateTime) {
return true;
}
}
}

return false;
}

signed main() {
scanf("%lld%lld", &planetCount, &planCount);
int binarySearchLeft = 0, binarySearchRight = 0, mid;

// 预处理log表
for (int i = 2; i <= planetCount; i++) {
logTable[i] = logTable[i >> 1] + 1;
}

// 读取星球间的航道信息
for (int i = 1; i < planetCount; i++) {
int planetA, planetB, travelTime;
scanf("%lld%lld%lld", &planetA, &planetB, &travelTime);
adj[planetA].push_back({planetB, travelTime});
adj[planetB].push_back({planetA, travelTime});
binarySearchLeft = max(binarySearchLeft, travelTime);
}

// 预处理DFS
preprocessDFS(1, 0);

// 预处理倍增数组
for (int i = 1; i <= logTable[planetCount]; i++) {
for (int j = 1; j <= planetCount; j++) {
ancestor[j][i] = ancestor[ancestor[j][i-1]][i-1];
}
}

// 处理每个运输计划,计算路径长度和LCA
for (int i = 1; i <= planCount; i++) {
scanf("%lld%lld", &plans[i].start, &plans[i].end);
planLCA[i] = findLCA(plans[i].start, plans[i].end);
planDistance[i] = nodeDistance[plans[i].start] + nodeDistance[plans[i].end] - 2 * nodeDistance[planLCA[i]];
binarySearchRight = max(binarySearchRight, planDistance[i]);
}

maxPlanLength = binarySearchRight;
binarySearchLeft = binarySearchRight - binarySearchLeft;

// 二分查找最小可行时间
while (binarySearchLeft < binarySearchRight) {
mid = (binarySearchLeft + binarySearchRight) >> 1;
if (checkFeasible(mid)) {
binarySearchRight = mid;
} else {
binarySearchLeft = mid + 1;
}
}

cout << binarySearchRight;
return 0;
}

http://www.dtcms.com/a/289817.html

相关文章:

  • 【66】MFC入门到精通——(CComboBox)下拉框选项顺序与添加顺序不一致
  • 前端静态资源免费cdn服务推荐
  • Dify极简部署手册
  • 30天打好数模基础-逻辑回归讲解
  • 7-大语言模型—指令理解:指令微调训练+模型微调
  • 【算法训练营Day15】二叉树part5
  • 编程研发工作日记
  • 050_Set接口(HashSet / TreeSet / LinkedHashSet)
  • 力扣面试150题--搜索插入位置
  • 某市公安局视频图像信息综合应用平台设计方案Word(446页)
  • AI产品经理面试宝典第40天:用户反馈处理与技术应用面试题与答法
  • 多校2+多校1的遗珠
  • 信道相关系数
  • 安装kali时出现“安装步骤失败“如何解决及后续软件安装
  • Python自动化测试项目实战
  • QT项目-仿QQ音乐的音乐播放器(第一节)
  • 什么是卡贴???
  • 国产电钢琴性价比实战选购指南
  • Python 虚拟环境与构建工具全景指南:功能介绍与对比分析
  • 基于Transformer的心理健康对话系统:从零构建AI心理咨询助手
  • 【全球甲烷估算模型】简化一箱模型(1-box model)
  • MySQL中的排序和分页
  • [simdjson] 实现不同CPU调度 | 自动硬件适配的抽象
  • C 语言经典编程题实战:从基础算法到趣味问题全解析
  • MybatisPlus-09.核心功能-IService开发复杂业务接口
  • 论文阅读:BLIP-2 (2023.4)
  • KOSMOS-2: 将多模态大型语言模型与世界对接
  • 第一章: 初识 Redis:背后的特性和典型应用场景
  • 你的created_time字段,用DATETIME还是TIMESTAMP?
  • brpc的安装与使用介绍以及channel的封装