[COCI2013] hiperprostor 题解

news/2024/10/11 8:23:02

前言

题目链接:Hydro & bzoj;黑暗爆炸。

题意简述

\(n\) 个点 \(m\) 条边的有向图上,第 \(i\) 条边的边权被表示为 \(k_i x + b_i\),其中 \(x\) 为一正整数。有 \(q\) 次询问,求出当 \(x\) 取值不同时,\(S\)\(T\) 最短路的值有多少种,以及和为多少。如果最短路的值有无限种可能,输出 inf。可能不能从 \(S\) 到达 \(T\)

\(n \leq 500\)\(m \leq 10000\)\(q \leq 10\)\(0 \leq b_i \leq 10^6\)\(0 \leq k_i \leq 10\)

原题 \(k_i \in \{ 0, 1 \}\),且 \(k_i = 1\) 时,\(b_i = 0\)

题目分析

询问很少,只要不太离谱的做法应该都是正确的。考虑一次询问,很容易想到分层图跑最短路。对 \(x\) 前面的系数分层,求出 \(dis_{k, u}\) 表示 \(x\) 前的系数为 \(k\) 时,走到 \(u\) 的最短路的常数项,即此时最短路为 \(kx + dis_{k, u}\)

注意到,\(k\) 不是无上界的,一个可能取到的上界为所有边的 \(k_i\) 之和,另一个取不到的上界是走了 \(n\) 条最大的边,不妨将其记作 \(K = \min \{ \sum \limits _ {i = 1} ^ m k_i, n \max k_i \}\)

不妨先考虑什么时候无解。此时一定有 \(\forall k \in [0, K], dis_{k, T} = \infty\)

以及无穷解。此时 \(S\)\(T\) 的最短路完全依赖于 \(x\) 的值。换句话说,不能不经过一条含有 \(x\) 的边到达 \(T\),即 \(dis_{0, T} = \infty\)

对于 \(T\) 来说,对于每一个 \(dis_{i,T} \neq \infty\)\(i\),我们有直线 \(f_i(x) = i x + dis_{i, T}\)。将这些直线在平面直角坐标系中绘出,对于一个 \(x\) 的取值,我们找到 \(\min f_i(x)\) 就是此时最短路长度。将所有 \((x, \min f_i(x))\) 连起来,就构成了一个上凸包。容易发现,凸包的边数是 \(\mathcal{O}(K)\) 的。

凸包

(你是不是感觉最上面那条线是倾斜的?其实它是水平的。我还怀疑这图怎么会画错了呢。)

如何求解放到之后讲,先来说说怎么统计答案。我们发现,在凸包两个相邻的拐点之间,其实是算一个一次函数在这段区间的函数值之和,是个等差数列,可以 \(\Theta(1)\) 计算。

剩下是求解凸包过程。平常我们的凸包是对着点集搞的,而这次是求出一堆直线围成的凸包,所以会有些不一样。考虑从大到小枚举直线的斜率,类似点集,维护一个单调栈,栈中的就是目前凸包中的直线,考虑新增一条直线的过程,什么时候栈顶直线不可能成为凸包的一部分呢?

仍能成为凸包的一部分

我们发现,在紫色这一段区间内,栈顶直线仍然是凸包的一部分。

不能成为凸包的一部分

此时,栈顶直线已经毫无用处了,因为在 \(top - 1\) 之后,新增直线永远比 \(top\) 更优。读者已经发现了,在直线交点处我们用蓝色做了标记,如果新增直线和 \(top\) 的交点在 \(top\)\(top - 1\) 的交点右侧,说明在这两个交点间,\(top\) 是凸包的一部分,反之则要弹出栈顶。

事实上,这和决策单调性有着异曲同工之妙。在决策单调性时,我们二分出 \(i\)\(j\) 更优的第一个位置,相当于我们这次求出的直线交点,弹栈操作也是换汤不换药的。

如此,在一个询问内我们可以做到 \(\Theta(Kn \log m)\) 的时间复杂度。

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <sstream>
#include <queue>
#include <set>
using namespace std;int n, m, q;struct Graph {struct node {int to, w1, w2, nxt;} edge[10010 << 1];int tot, head[520];void add(int u, int v, int w1, int w2) {edge[++tot] = { v, w1, w2, head[u] };head[u] = tot;}inline node & operator [] (const int x) { return edge[x]; }
} xym;int dis[10010][520];int totX;template <typename T>
using minHeap = priority_queue<T, vector<T>, greater<T>>;using lint = long long;lint cnt, sum;inline lint solve(int k, int b, int x) {// 计算一次函数值return 1ll * k * x + b;
}inline void solve(int k, int b, int l, int r) {// 计算一次函数在 [l, r] 的函数值之和l = max(l, 1);if (l > r) return;cnt += r - l + 1;sum += (solve(k, b, l) + solve(k, b, r)) * (r - l + 1) / 2;// 等差数列
}inline double jiao(int k1, int b1, int k2, int b2) {// 两直线交点return 1.0 * (b2 - b1) / (k1 - k2);
}inline int jjiao(int k1, int b1, int k2, int b2) {// 最大的小于两直线交点横坐标的整数return max(0, (b2 - b1 - 1) / (k1 - k2));
}int s, t;
int hill[520], tot;inline double jiao(int a, int b) {return jiao(a, dis[a][t], b, dis[b][t]);
}signed main() {scanf("%d%d", &n, &m);for (int i = 1, u, v, w1, w2; i <= m; ++i) {static char str[10];scanf("%d%d%s", &u, &v, str);if (*str == 'x') {w1 = 1, w2 = 0;++totX;} else {stringstream sin(str);sin >> w2, w1 = 0;}xym.add(u, v, w1, w2);}totX = min(totX, n);scanf("%d", &q);for (; q--;) {scanf("%d%d", &s, &t);#ifdef XuYuemingprintf(">>>>>>>>>>> ");#endifif (s == t) {printf("1 0\n");continue;}memset(dis, 0x3f, sizeof dis);dis[0][s] = 0;minHeap<pair<int, pair<int, int>>> Q;Q.push({ 0, { 0, s } });while (!Q.empty()) {auto [ndis, _now] = Q.top();Q.pop();auto [xcnt, now] = _now;if (now == t || ndis > dis[xcnt][now] || xcnt > totX)continue;for (int i = xym.head[now]; i; i = xym[i].nxt) {int tx = xcnt + xym[i].w1;int len = dis[xcnt][now] + xym[i].w2;int to = xym[i].to;if (dis[tx][to] > len) {dis[tx][to] = len;Q.push({ len, { tx, to } });}}}bool can = false;for (int i = 0; !can && i <= totX; ++i)can |= dis[i][t] != 0x3f3f3f3f;if (!can) {puts("0 0");continue;}if (dis[0][t] == 0x3f3f3f3f) {puts("inf");continue;}// 有若干形如 f_i(x) = ix + dis[i][t] 的直线// 将这些直线在平面直角坐标系中绘出。// 对于一个 $x$ 的取值,我们找到 $\min f_i(x)$ 就是此时最短路长度// 将所有 $(x, \min f_i(x))$ 连起来,就构成了一个上凸包。tot = 0;for (int i = totX; i >= 0; --i) if (dis[i][t] != 0x3f3f3f3f) {while (tot - 1 >= 1 && jiao(hill[tot - 1], hill[tot]) >= jiao(hill[tot], i))--tot;  // 此时栈顶直线不可能成为凸包的一部分,类似于决策单调性// 直线交点就是 i 比 j 更优的第一个位置hill[++tot] = i;  // 把这条直线加入凸包}cnt = 1, sum = dis[0][t];for (int i = 1, lst = 1; i + 1 <= tot; ++i) {// 计算 [上一次交点, 这一次交点) 的函数值之和int cur = jjiao(hill[i], dis[hill[i]][t], hill[i + 1], dis[hill[i + 1]][t]);// 求出小于交点横坐标的最大整数solve(hill[i], dis[hill[i]][t], lst, cur);lst = cur + 1;  // 更新上一次交点}printf("%lld %lld\n", cnt, sum);}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

c10-8.24课程作业

4、以下哪个口令不是弱口令? D

生产者消费者模式,以及基于BlockingQueue的快速实现

生产者消费者模式,以及基于BlockingQueue的快速实现什么是生产者消费者模式,简单来说就是有两个角色,一个角色主要负责生产数据,一个角色主要负责消费(使用)数据。那么生产者直接依赖消费者,然后直接调用是否可以?答案是可以的,但是有些场景无法及时解决,典型的就是生…

ETL语句

ETL(Extract, Transform, Load)是数据处理工作的重要组成部分,即提取、转换、加载(从一个地方提取数据,通过转换,加载到另一个地方)。通常可以使用SQL语句实现

一张报表完成工厂生产综合数据分析,用这款免费报表工具就够了

在当今快节奏的工业环境中,工厂管理者们越来越依赖于数据分析来优化生产流程、提高效率和降低成本。然而,传统的数据分析工具往往复杂难用,且动辄需要高昂的费用,这让很多工厂望而却步。不过最近本人发现了一款非常实用的报表工具,叫作山海鲸可视化(官网:shanhaibi.com/…

RE入门第四天---做新手题

题目来自polarDN wp来自: PolarCTF靶场Reverse方向简单难度Writeup - 这里是千夏 (l0serqianxia.github.io) polar靶场reverse区简单难度题目详解 - 先知社区 (aliyun.com) shell 考查:UPX自动脱壳 下载下来 ida打开有壳的体现 尝试自动脱壳 D:\..CTFgoju\reverse\UPX\upx-4.…

笔记——字符串

蓝月の笔记——字符串篇 摘要 一些串串 \(\quad\qquad\)——某yl新高一学长 字串 \(\quad\qquad\)——某yl新高一学长のppt Warning 本文中字符串的下标有时从 \(1\) 开始有时从 \(0\) 开始,请自行分辨无特殊说明从 \(1\) 开始 字符串长度无特殊说明为 \(n\) 字符串无特殊说明…

MySQL 2003 - Can’t connect to MySQL server on (10060)

2003 - Can’t connect to MySQL server on (10060)一般是以下几个原因造成的: 1.网络不通畅 2.mysql 服务未启动 3.防火墙未开放端口 4 ##云服务器的 安全组规则未设置一般是以下几个原因造成的: 1.网络不通畅:【mysql -u -p, 看看能不能登陆 】 2.mysql 服务未启动:【my…

详解 dotenv 的使用与实现

每当涉及到保护API密钥或我们不想因为开源项目而向公众展示的东西时,我们总是倾向于.env文件,而它每当涉及到保护API密钥或我们不想因为开源项目而向公众展示的东西时,我们总是倾向于.env文件,而它的解析依赖到dotenv包,一个每周都有31k+开发人员下载的软件包。其设计的理…