P2483 【模板】k 短路 / [SDOI2010] 魔法猪学院

题目:

题目背景
注:对于 kk 短路问题,A* 算法的最坏时间复杂度是 O(nk \log n)O(nklogn) 的。虽然 A* 算法可以通过本题原版数据,但可以构造数据,使得 A* 算法在原题的数据范围内无法通过。事实上,存在使用可持久化可并堆的算法可以做到在 O((n+m) \log n + k \log k)O((n+m)logn+klogk) 的时间复杂度解决 kk 短路问题。详情见 OI-Wiki。

题目描述
iPig 在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练。经过了一周理论知识和一周基本魔法的学习之后,iPig 对猪世界的世界本原有了很多的了解:众所周知,世界是由元素构成的;元素与元素之间可以互相转换;能量守恒\ldots…。

iPig 今天就在进行一个麻烦的测验。iPig 在之前的学习中已经知道了很多种元素,并学会了可以转化这些元素的魔法,每种魔法需要消耗 iPig 一定的能量。作为 PKU 的顶尖学猪,让 iPig 用最少的能量完成从一种元素转换到另一种元素\ldots…等等,iPig 的魔法导猪可没这么笨!这一次,他给 iPig 带来了很多 11 号元素的样本,要求 iPig 使用学习过的魔法将它们一个个转化为 NN 号元素,为了增加难度,要求每份样本的转换过程都不相同。这个看似困难的任务实际上对 iPig 并没有挑战性,因为,他有坚实的后盾\ldots…现在的你呀!

注意,两个元素之间的转化可能有多种魔法,转化是单向的。转化的过程中,可以转化到一个元素(包括开始元素)多次,但是一但转化到目标元素,则一份样本的转化过程结束。iPig 的总能量是有限的,所以最多能够转换的样本数一定是一个有限数。具体请参看样例。

输入格式
第一行三个数 N, M, EN,M,E,表示 iPig 知道的元素个数(元素从 11 到 NN 编号),iPig 已经学会的魔法个数和 iPig 的总能量。

后跟 MM 行每行三个数 s_i, t_i, e_is
i

,t
i

,e
i

表示 iPig 知道一种魔法,消耗 e_ie
i

的能量将元素 s_is
i

变换到元素 t_it
i

输出格式
一行一个数,表示最多可以完成的方式数。输入数据保证至少可以完成一种方式。

输入输出样例
输入 #1复制
4 6 14.9
1 2 1.5
2 1 1.5
1 3 3
2 3 1.5
3 4 1.5
1 4 1.5
输出 #1复制
3
说明/提示
有意义的转换方式共 44 种:

1\to 41→4,消耗能量 1.51.5。

1\to 2\to 1\to 41→2→1→4,消耗能量 4.54.5。

1\to3\to41→3→4,消耗能量 4.54.5。

1\to2\to3\to41→2→3→4,消耗能量 4.54.5。

显然最多只能完成其中的 33 种转换方式(选第一种方式,后三种方式任选两个),即最多可以转换 33 份样本。

如果将 E=14.9E=14.9 改为 E=15E=15,则可以完成以上全部方式,答案变为 44。

数据规模
占总分不小于 10%10% 的数据满足 N \leq 6,M \leq 15N≤6,M≤15。

占总分不小于 20%20% 的数据满足 N \leq 100,M \leq 300,E\leq100N≤100,M≤300,E≤100 且 EE 和所有的 e_ie
i

均为整数(可以直接作为整型数字读入)。

所有数据满足 2 \leq N \leq 50002≤N≤5000,1 \leq M \leq 2000001≤M≤200000,1 \leq E \leq 10 ^ 71≤E≤10
7
,1 \leq ei\leq E1≤ei≤E,EE 和所有的 e_ie
i

为实数。

解析

对于原图以 tt 为根建出任意一棵最短路径树 TT,即反着从 tt 跑出到所有点的最短路 disdis

它有一些性质:

性质1:

对于一条 ss 到 tt 的路径的边集 PP,去掉 PP 中和 TT 的交集,记为 P’P

那么 P’P

对于中任意相邻(从 ss 到 tt 的顺序)的两条边 e,fe,f,满足 ff 的起点在 TT 中为 ee 的终点的祖先或者为相同点。

因为 PP 中 e,fe,f 之间由树边相连或者直接相连。

性质2:

对于不在 TT 中的边 ee ,设 uu 为起点,vv 为终点,ww为权值。

定义 \Delta_e=dis_v+w-dis_uΔ
e

=dis
v

+w−dis
u

,即选这条边的路径和最短路的长度的差

设 L_PL
P

表示路径长度,则有

L_P=dis_s+\sum_{e\in p’}\Delta_e
L
P

=dis
s

+
e∈p



Δ
e

这很显然。

性质3:

对于满足性质 11 的 P’P

的定义的边集 SS,有且仅有一条 ss 到 tt 的路径的边集 PP,使得 P’=SP

=S。

因为树 TT 上的两个点之间有且仅有一条路径。

问题转化

求第 kk 小的满足性质 11 的 P’P

的定义的边集

算法

用小根堆维护边集 PP

初始 PP 为空集(实际上只要维护边集当前尾部的边的起点是哪一个就好了,空集即 ss)

每次取出最小权值的边集 PP,设当前尾部的边的起点为 xx

有两种方法可以得到一个新的边集:

**1.**替换 xx 为起点的这条边为一条刚好大于等于它的非树边。

**2.**尾部接上一条起点为以 xx 为起点的这条边的终点在 TT 中祖先(包括自己)连出去的所有非树边的最小边。

然后就是怎么维护祖先出去的所有非树边的最小边:

显然可以从祖先转移过来,直接可并堆即可。

又因为要保留每个点的信息,所以合并的时候可持久化即可

和线段树合并的可持久化一样,然后就可以过了。

建议可以看一看课件

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;template <class Num> inline void Cmax(Num &x, const Num y) {x = y > x ? y : x;
}template <class Num> inline void Cmin(Num &x, const Num y) {x = y < x ? y : x;
}const int maxn(5005);
const int maxm(2e5 + 5);
const double eps(1e-8);int n, m, first[maxn], cnt, vis[maxn], rt[maxn], tot, cov[maxm << 1], ans, fa[maxn];
double se, e, dis[maxn];
priority_queue < pair <double, int> > q;struct Heap {int ls, rs, dis, ed;double w;
} tr[maxm * 20];struct Edge {int to, next;double w;
} edge[maxm << 1];inline void Add(int u, int v, double w) {edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
}inline int NewNode(double w, int ed) {int x = ++tot;tr[x].w = w, tr[x].dis = 1, tr[x].ed = ed;return x;
}int Merge(int x, int y) {if (!x || !y) return x + y;if (tr[x].w - tr[y].w >= eps) swap(x, y);int p = ++tot;tr[p] = tr[x], tr[p].rs = Merge(tr[p].rs, y);if (tr[tr[p].ls].dis < tr[tr[p].rs].dis) swap(tr[p].ls, tr[p].rs);tr[p].dis = tr[tr[x].rs].dis + 1;return p;
}void Dfs(int u) {vis[u] = 1;for (int e = first[u], v; e != -1; e = edge[e].next)if (e & 1) {double w = edge[e].w;if (fabs(dis[u] + w - dis[v = edge[e].to]) < eps && !vis[v])fa[v] = u, cov[e ^ 1] = 1, Dfs(v);}
}int main() {memset(first, -1, sizeof(first));memset(dis, 127, sizeof(dis));scanf("%d%d%lf", &n, &m, &se);for (int i = 1, u, v; i <= m; ++i) scanf("%d%d%lf", &u, &v, &e), Add(u, v, e);dis[n] = 0, q.push(make_pair(0, n));while (!q.empty()) {int u = q.top().second;q.pop();if (vis[u]) continue;vis[u] = 1;for (int e = first[u]; ~e; e = edge[e].next)if (e & 1) {int v = edge[e].to;if (dis[v] - (dis[u] + edge[e].w) >= eps)q.push(make_pair(-(dis[v] = dis[u] + edge[e].w), v));}}for (int i = 1; i <= n; ++i) vis[i] = 0;Dfs(n);for (int e = 0, u, v; e < cnt; e += 2)if (!cov[e]) {u = edge[e ^ 1].to, v = edge[e].to;if (dis[u] == dis[0] || dis[v] == dis[0]) continue;rt[u] = Merge(rt[u], NewNode(dis[v] + edge[e].w - dis[u], v));}for (int i = 1; i <= n; ++i) q.push(make_pair(-dis[i], i));for (int i = 1, u; i <= n; ++i) {u = q.top().second, q.pop();if (fa[u]) rt[u] = Merge(rt[u], rt[fa[u]]);}if (dis[1] - se < eps) se -= dis[1], ++ans;if (rt[1]) q.push(make_pair(-tr[rt[1]].w, rt[1]));while (!q.empty()) {int ed = q.top().second;double cur = q.top().first, w = dis[1] - cur;if (w - se >= eps) break;q.pop(), se -= w, ++ans;for (int i = 0; i < 2; ++i) {int nxt = i ? tr[ed].rs : tr[ed].ls;if (nxt) q.push(make_pair(cur + tr[ed].w - tr[nxt].w, nxt));}if (rt[tr[ed].ed]) q.push(make_pair(cur - tr[rt[tr[ed].ed]].w, rt[tr[ed].ed]));}printf("%d\n", ans);return 0;
}

拜拜!!

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

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

相关文章

部署ELFK+zookeeper+kafka架构

目录 前言 一、环境部署 二、部署ELFK 1、ELFK ElasticSearch 集群部署 1.1 配置本地hosts文件 1.2 安装 elasticsearch-rpm 包并加载系统服务 1.3 修改 elasticsearch 主配置文件 1.4 创建数据存放路径并授权 1.5 启动elasticsearch是否成功开启 1.6 查看节点信息 …

蓝桥杯2023年第十四届省赛真题-冶炼金属

思路&#xff1a;用二分模板&#xff0c;边界r得包含所有的v&#xff0c;check分为小于&#xff0c;大于&#xff0c;等于三种情况。 #include<bits/stdc.h> using namespace std; #define int long long #define endl \n int n; const int N 1e410; int a[N],b[N];int…

【Conda基础命令】使用conda创建、查看、删除虚拟环境及可能的报错处理

文章目录 前言&#xff08;1&#xff09; 在默认路径下创建一个新的虚拟环境&#xff08;2&#xff09; 查看已有的虚拟环境&#xff08;3&#xff09; 删除已有的虚拟环境&#xff08;谨慎操作&#xff09;&#xff08;4&#xff09;激活虚拟环境&#xff08;5&#xff09;退出…

数据结构(七)---树

目录 一.树的基本概念 二.树的性质 三.二叉树 1.二叉树的基本概念 2.特殊的二叉树 &#xff08;1&#xff09;满二叉树 &#xff08;2&#xff09;完全二叉树 &#xff08;3&#xff09;二叉排序树 &#xff08;4&#xff09;平衡二叉树 3.二叉树的性质 4.完全二叉树…

【运维自动化-配置平台】如何创建业务及拓扑(集群-模块)

业务&#xff0c;是蓝鲸 CD 体系中比较重要的概念和维度&#xff0c;日常使用中主机、进程、业务拓扑的管理都需要依赖已经存在的业务&#xff0c;其他蓝鲸体系产品也基本上都是围绕业务的维度来提供对应的服务和相关的鉴权。1、创建业务/业务集 请确保有创建业务的权限&#…

关于CAS

什么是CAS: CAS:Compare And Swap&#xff0c;比较且交换。 CAS中有三个参数&#xff1a;1.内存中原数据的值V 2.预期值A 3.修改后的数据B Compare&#xff1a;V与A会先比较是否一样 Swap&#xff1a;如果V与A一致&#xff0c;那么就将B写入V 返回操作是否成功 伪代码&…

[生活][杂项] 如何正确打开编织袋

编织袋打开的正确姿势 面对单线分离右边的线头&#xff0c;然后依次拉开即可

java 中的 Math.round(-1.5) 等于多少?

Math提供了三个与取整有关的方法&#xff1a;ceil、floor、round &#xff08;1&#xff09;ceil&#xff1a;向上取整&#xff1b; Math.ceil(11.3) 12; Math.ceil(-11.3) 11; &#xff08;2&#xff09;floor&#xff1a;向下取整&#xff1b; Math.floor(11.3) 11; …

产品推荐 | 基于Lattice用于原型和FPGA设计和开发的Avant-E 评估板

01 产品概述 莱迪思半导体Avant-E评估板使设计人员能够快速进行原型设计和FPGA设计测试。它提供对所有 I/O 的访问&#xff0c;以及广泛的内存选项&#xff0c;以实现更快的原型设计和开发。 Avant-E评估板采用LFG1156封装的Avant-E FPGA。该板可以通过 FMC HPC、PMAD 和 Ras…

vue--样式绑定--样式切换方法

1.通过改变类名的方法改变盒子样式 可以通过 :class变量名来动态改变标签的样式名&#xff0c;变量值可以是字符串、数组、对象 1.字符串写法 适用于样式类名不确定需要动态指定 <div classbase :classa>Text</div> data:{ a:normal } classbase和 :classa可…

【C语言】数组相关选择题

目录 前言 题目一&#xff1a; 题目二&#xff1a; 题目三&#xff1a; 题目四&#xff1a; 题目五&#xff1a; 题目六&#xff1a; 题目七&#xff1a; 题目八&#xff1a; 题目九&#xff1a; 题目十&#xff1a; 前言 一些数组选择题 题目一&#xff1a; 关于数…

Java程序实现多语言资源JSON文件生成

你好呀&#xff0c;我是小邹。 在现代软件开发中&#xff0c;实现应用程序的多语言支持是一项基本需求&#xff0c;以适应全球用户的语言环境。本文将介绍一段Java代码&#xff0c;其主要功能是生成一个特定格式的JSON文件&#xff0c;用于存储和管理中英文双语对照的键值对&a…

Claude 3 Opus 效果是否真的可以超过GPT-4?

实测,不仅是超过,而且我个人感觉这个差距甚至大于GPT3.5到GPT4的距离. claude3在长篇理学论文的解析能力是非常显著的,可以扩展补完作者省略的大量运用高等数学,复变函数以及更多数理方法的计算过程,并且将中间过程补完的非常完美.不会漏符号,错符号,偏差数值之类的问题.工科许…

ModuleNotFoundError: No module named ‘cv2‘

python运行到下面这一句“import cv2”时报错&#xff1a; >>> import cv2 Traceback (most recent call last):File "<stdin>", line 1, in <module> ModuleNotFoundError: No module named cv2 解决方法&#xff1a; 安装扩展包&#xff0…

基于K-prototype算法聚类

k-prototype聚类是一种用于混合数据类型聚类的算法&#xff0c;由Jain和Dubes在1988年提出。它主要用于同时包含连续属性和离散属性的数据集。k-prototype算法可以看作是k-means算法的扩展&#xff0c;它将k-means算法的思想应用于混合数据类型&#xff0c;通过为连续属性和离散…

ubuntu在xshell中使用快捷方式操作命令,减少命令行的数入量

第一步 第二步 然后无脑确定 第三步 在xshell的显示方式 方式一 这样就会在每个窗格中进行显示 方式二 效果显示–> 这种窗格的显示是全局的 然后你双击这个process就会自动把命令打在命令行上&#xff0c;减少你的输入量

Oracle数据库Bug:相关子查询多层嵌套报错:标识符无效

Oracle Bug? 一、案例描述二、解决方案<一>、升级版本<二>、改写语句 一、案例描述 在Mysql中常常有如下写法用相关子查询 order by desc limit 1来完成需求 select code,date,(select value from test t1 where t.code t1.code and t1.date between date_su…

【Gradle如何安装配置及使用的教程】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

新型大数据架构之湖仓一体(Lakehouse)架构特性说明——Lakehouse 架构(一)

文章目录 为什么需要新的数据架构&#xff1f;湖仓一体&#xff08;Lakehouse&#xff09;——新的大数据架构模式同时具备数仓与数据湖的优点湖仓一体架构存储层计算层 湖仓一体特性单一存储拥有数据仓库的查询性能存算分离开放式架构支持各种数据源类型支持各种使用方式架构简…

递归求阶乘和(不熟悉)

本题要求实现一个计算非负整数阶乘的简单函数&#xff0c;并利用该函数求 1!2!3!...n! 的值。 函数接口定义&#xff1a; double fact( int n ); double factsum( int n ); 函数fact应返回n的阶乘&#xff0c;建议用递归实现。函数factsum应返回 1!2!...n! 的值。题目保证输…