LOJ #3490. 「JOISC 2021 Day2」逃跑路线

news/2024/9/18 21:37:25/文章来源:https://www.cnblogs.com/Scarab/p/18419371

Description

IOI 王国的国民使用 Byou 作为时间单位。

IOI 王国中,一天被划分为 \(S\) Byou,其中第 \(x\) 个 Byou(\(0\leq x < S\)) 被称为时刻 \(x\)

IOI 王国中有 \(N\) 个城市(标号 \(0\sim N-1\))和 \(M\) 条双向道路(标号 \(0 \sim M-1\)),你可以通过道路从一个城市移动到另一个城市。第 \(i\) 条道路直接连接着城市 \(A_i\)\(B_i\),通过这条道路需要 \(L_i\) Byou。每天,从时刻 \(C_i\) 开始要对第 \(i\) 条道路进行检查,直到这一天结束。

JOI 集团是 IOI 王国中的一个秘密组织,出于其保密性,JOI 集团的成员不应该在道路上受到检查,这意味着如果 JOI 王国的成员如果想要通过道路 \(i\),那么他必须在时刻 \(C_i-L_i\) 之前踏上这条路。

现在有 \(Q\) 名 JOI 集团的成员(标号 \(0 \sim Q-1\)),第 \(j\) 名成员在某一天的时刻 \(T_j\) 想要从城市 \(U_j\) 出发旅行去城市 \(V_j\)。成员可以在城市内停留任意长的时间。这名成员可能要花费多天才能到达城市 \(V_j\)

现要求计算每个成员完成旅行所需要的最少时间。

  • \(2 \leq N \leq 90\)
  • \(N-1 \leq M \leq \dfrac{N(N-1)}{2}\)
  • \(2 \leq S \leq 10^{15}\)
  • \(1 \leq Q \leq 3 \times 10^6\)
  • \(0 \leq A_i,B_i \leq N-1 (0 \leq i \leq M-1)\)
  • \(A_i \ne B_i(0 \leq i \leq M-1)\)
  • \(\forall i,j \in [0,M-1]\),若 \(i \ne j\),则有 \((A_i,B_i) \ne (A_j,B_j),(A_i,B_i) \ne (B_j,A_j)\)。。
  • \(1 \leq L_i \leq C_i < S\)
  • 你可以从某个城市经过若干条道路到达任意另一个城市。
  • \(0\leq U_j,V_j \leq N-1(0 \leq j \leq Q-1)\)
  • \(U_j \ne V_j\)
  • \(0 \leq T_j < S\)

Solution

首先有个单次询问 \(O(n^2+m)\) 的做法是直接暴力跑 dijkstra 最短路。显然过不了。

考虑优化。

刚才那个做法主要慢在没有任何预处理而每次重新做询问,导致询问时间复杂度过高而超时。注意到当一个点第一次被道路卡后,时间\(\bmod S\) 一定为 \(0\),这样起点的状态只有 \(O(n)\) 种,可以预处理了。

根据上面那个做法可以将答案路径分为两种:在一天内走完、走至少两天。

先考虑第一种情况,对于第二种情况可以枚举第一天走的最后的点,就转化为了第一天和初始时间为 \(0\) 的情况了。

同样是预处理。固定起点和终点,对于一组方案,设 \((u,v,l,c)\) 为方案中经过的边,\(x\) 为到 \(u\) 的时间,将初始时间往后移 \([0,c-l-x]\) 这条边仍然能走,所以每次找到 \(c-l-x\) 最小的边删掉,在剩下的边继续跑即可预处理出所有初始时间区间的答案。时间复杂度:\(O(n^5)\),过不了。

显然可以枚举瓶颈边 \((u,v,l,c)\),则到 \(u\) 的时间小于等于 \(c-l\)\(v\) 当天到后面点的时间等于 \(c\) 时刻从 \(v\) 出发的时间。所以可以设 \(dis1_x\) 表示当天从 \(x\)\(u\) 的最小时间,通过这个东西可以求出 \((u,v,l,c)\) 为瓶颈边的最大初始时刻。\(dis2_x\) 表示当天 \(c\) 时刻从 \(v\)\(x\) 的最小时间。于是 \(x\to y\) 的路径中瓶颈边为 \((u,v,l,c)\) 最小时间为 \(dis1_x+dis2_y+l\)

然后搞个 vector 维护每对起点和终点经过所有瓶颈边对应的最小时间,先按照初始时间的限制排序,查询时二分出可行的后缀即可。

如果至少走两天,可以设 \(f_{x,y}\) 表示当天从 \(y\)\(x\) 的最小时间,\(g_{x,y}\) 表示 \(0\) 时刻从 \(x\)\(y\) 的最小时间,这两个数组的求法和上面瓶颈边的 \(dis1,dis2\) 差不多,然后枚举中转点 \(i\),贡献即为 \(\min\limits_{s-f_{u,i}\geq t}{s+g_{v,i}-t}\)

具体细节见代码。

时间复杂度:\(O(n^4+qn+q\log n)\)

Code

#include <bits/stdc++.h>// #define int int64_tnamespace FASTIO {
char ibuf[1 << 21], *p1 = ibuf, *p2 = ibuf;
char getc() {return p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
template<class T> bool read(T &x) {x = 0; int f = 0; char ch = getc();while (ch < '0' || ch > '9') f |= ch == '-', ch = getc();while (ch >= '0' && ch <= '9') x = (x * 10) + (ch ^ 48), ch = getc();x = (f ? -x : x); return 1;
}
template<typename A, typename ...B> bool read(A &x, B &...y) { return read(x) && read(y...); }char obuf[1 << 21], *o1 = obuf, *o2 = obuf + (1 << 21) - 1;
void flush() { fwrite(obuf, 1, o1 - obuf, stdout), o1 = obuf; }
void putc(char x) { *o1++ = x; if (o1 == o2) flush(); }
template<class T> void write(T x) {if (!x) putc('0');if (x < 0) x = -x, putc('-');char c[40]; int tot = 0;while (x) c[++tot] = x % 10, x /= 10;for (int i = tot; i; --i) putc(c[i] + '0');
}
void write(char x) { putc(x); }
void write(char *x) { while (*x) putc(*x++); }
void write(const char *x) { while (*x) putc(*x++); }
template<typename A, typename ...B> void write(A x, B ...y) { write(x), write(y...); }
struct Flusher {~Flusher() { flush(); }
} flusher;
} // namespace FASTIO
using FASTIO::read; using FASTIO::putc; using FASTIO::write;const int kMaxN = 95, kMaxM = kMaxN * kMaxN / 2;
const int64_t kInf = 1e18;int n, m, q;
int u[kMaxM], v[kMaxM];
int64_t s, l[kMaxM], c[kMaxM], f[kMaxN][kMaxN], g[kMaxN][kMaxN], dis1[kMaxN], dis2[kMaxN];
std::vector<std::tuple<int, int64_t, int64_t>> G[kMaxN];
std::vector<std::pair<int64_t, int64_t>> vec[kMaxN][kMaxN];void dijkstra1(int x, int64_t t) {static bool vis[kMaxN];for (int i = 1; i <= n; ++i) {dis1[i] = kInf, vis[i] = 0;}std::queue<int> q;q.emplace(x), dis1[x] = 0;for (int c = 1; c <= n; ++c) {int u = 0;for (int i = 1; i <= n; ++i)if (!vis[i] && (!u || dis1[i] < dis1[u]))u = i;if (!u || dis1[u] >= 1e17) break;vis[u] = 1;for (auto [v, l, c] : G[u]) {if (t - dis1[u] - l < 0) continue;if (t - dis1[u] <= c) dis1[v] = std::min(dis1[v], dis1[u] + l);}}
}void dijkstra2(int x, int64_t t) {static bool vis[kMaxN];for (int i = 1; i <= n; ++i) {dis2[i] = kInf, vis[i] = 0;}std::queue<int> q;q.emplace(x), dis2[x] = 0;for (int c = 1; c <= n; ++c) {int u = 0;for (int i = 1; i <= n; ++i)if (!vis[i] && (!u || dis2[i] < dis2[u]))u = i;if (!u || dis2[u] >= 1e17) break;vis[u] = 1;for (auto [v, l, c] : G[u]) {int64_t val = dis2[u] + t;if (val % s <= c - l) dis2[v] = std::min(dis2[v], dis2[u] + l);}}
} void dijkstra3(int x, int64_t t) {static bool vis[kMaxN];for (int i = 1; i <= n; ++i) {dis1[i] = kInf, vis[i] = 0;}std::queue<int> q;q.emplace(x), dis1[x] = 0;for (int c = 1; c <= n; ++c) {int u = 0;for (int i = 1; i <= n; ++i)if (!vis[i] && (!u || dis1[i] < dis1[u]))u = i;if (!u || dis1[u] >= 1e17) break;vis[u] = 1;for (auto [v, l, c] : G[u]) {if (t - dis1[u] - l < 0) continue;if (t - dis1[u] > c) dis1[v] = std::min(dis1[v], dis1[u] + t - dis1[u] - c + l);else dis1[v] = std::min(dis1[v], dis1[u] + l);}}
}void dijkstra4(int x, int64_t t) {static bool vis[kMaxN];for (int i = 1; i <= n; ++i) {dis2[i] = kInf, vis[i] = 0;}std::queue<int> q;q.emplace(x), dis2[x] = 0;for (int c = 1; c <= n; ++c) {int u = 0;for (int i = 1; i <= n; ++i)if (!vis[i] && (!u || dis2[i] < dis2[u]))u = i;if (!u || dis2[u] >= 1e17) break;vis[u] = 1;for (auto [v, l, c] : G[u]) {int64_t val = dis2[u] + t;if (val % s > c - l) val += s - val % s;dis2[v] = std::min(dis2[v], val - t + l);}}
}int64_t solve(int u, int v, int64_t t) {int64_t res = kInf;auto it = std::lower_bound(vec[u][v].begin(), vec[u][v].end(), std::pair<int64_t, int64_t>{t, 0});if (it != vec[u][v].end()) res = std::min(res, it->second);for (int i = 1; i <= n; ++i) {if (s - f[i][u] >= t) res = std::min(res, s + g[i][v] - t);}return res;
}void dickdreamer() {read(n, m, s, q);for (int i = 1; i <= m; ++i) {read(u[i], v[i], l[i], c[i]);++u[i], ++v[i];G[u[i]].emplace_back(v[i], l[i], c[i]), G[v[i]].emplace_back(u[i], l[i], c[i]);}for (int i = 1; i <= m; ++i) {dijkstra1(u[i], c[i] - l[i]), dijkstra2(v[i], c[i]);for (int j = 1; j <= n; ++j) {for (int k = 1; k <= n; ++k) {vec[j][k].emplace_back(c[i] - l[i] - dis1[j], dis1[j] + dis2[k] + l[i]);}}dijkstra1(v[i], c[i] - l[i]), dijkstra2(u[i], c[i]);for (int j = 1; j <= n; ++j) {for (int k = 1; k <= n; ++k) {vec[j][k].emplace_back(c[i] - l[i] - dis1[j], dis1[j] + dis2[k] + l[i]);}}}for (int i = 1; i <= n; ++i) {for (int j = 1; j <= n; ++j) {std::sort(vec[i][j].begin(), vec[i][j].end());for (int k = (int)vec[i][j].size() - 2; k >= 0; --k) {vec[i][j][k].second = std::min(vec[i][j][k].second, vec[i][j][k + 1].second);}}}for (int i = 1; i <= n; ++i) {dijkstra3(i, s), dijkstra4(i, 0);for (int j = 1; j <= n; ++j) {f[i][j] = dis1[j], g[i][j] = dis2[j];}}for (int c = 1; c <= q; ++c) {int u, v; int64_t t;read(u, v, t);++u, ++v;write(solve(u, v, t), '\n');}
}int32_t main() {
#ifdef ORZXKRfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifint T = 1;// std::cin >> T;while (T--) dickdreamer();// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";return 0;
}

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

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

相关文章

自动驾驶运动规划学习_碰撞检测算法_GJK

自动驾驶运动规划学习:碰撞检测算法:GJK Gilbert–Johnson–Keerthi(GJK)算法,是一种用于检测两个凸集是否重叠的高效算法,并且可以得到两个凸集的最小距离.1.4.1 GJK算法原理1.4.1.1 闵可夫斯基差(Minkowski Difference)1.4.1.3 凸性 在二维空间中,如果一个凸集包含原点,…

设计模式之——代理模式

代理模式 前言: 我们一般在租房子时会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做;再比如我们打官司需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法;再比如在淘宝上面买东西,你使用支付宝平台…

一文搞定WeakHashMap

写在前面 在缓存场景下,由于内存是有限的,不能缓存所有对象,因此就需要一定的删除机制,淘汰掉一些对象。这个时候可能很快就想到了各种Cache数据过期策略,目前也有一些优秀的包提供了功能丰富的Cache,比如Google的Guava Cache,它支持数据定期过期、LRU、LFU等策略,但它…

P2710 数列/P2042 [NOI2005] 维护数列

题意(以 P2710 为例)思路 使用 FHQ-Treap 进行求解,清晰明了。对于 insert,先将要插入的数建成一棵树,然后将这棵树放入 FHQ-Treap 中。 对于 delete,将要删除的树分离出来,然后把剩下的部分合并即可,将删除的树的树根丢到废弃节点的栈中以备以后使用(节约空间,不然 …

扩展分析C语言单双引号、反斜杠与注释

目录注释奇怪的注释C风格的注释无法嵌套一些特殊的注释注释的规则建议反斜杠\反斜杠有续行的作用,但要注意续行后不能添加空格回车也能起到换行的作用,那续行符的意义在哪?反斜杠的转义功能单引号和双引号字面值,字符串,字符,字符变量的大小为什么sizeof(1)的大小是4 ?char…

扩展分析单双引号、反斜杠与注释

目录注释奇怪的注释C风格的注释无法嵌套一些特殊的注释注释的规则建议反斜杠\反斜杠有续行的作用,但要注意续行后不能添加空格回车也能起到换行的作用,那续行符的意义在哪?反斜杠的转义功能单引号和双引号字面值,字符串,字符,字符变量的大小为什么sizeof(1)的大小是4 ?char…

C----函数递归之反汇编

环境 win10 vc6.0 debug 代码 关于求阶层问题:n!=n(n-1)!;(n-1)! = (n-1)(n-2)! 例如5!=5(4)! 4!=43! 3!=32! 2!=21 函数递归的出口是1,所以函数递归最重要的条件是去寻找递归的出口 int fun(int i) {int sum = 0;if (i == 1){return 1;}else{sum = i*fun(i-1);}return sum …

地平线占用预测 FlashOcc 参考算法-V1.0

1.简介 3D Occupancy Networks 的基本思路是将三维空间划分成体素网格,并对每个网格进行各类感知任务的预测。目前以网格为中心的方法能够预测每个网格单元的占用率、语义类别、未来运动位移和实例信息。3D occupancy 可以对道路障碍物进行更细粒度的划分,同时获取更精确的占…

手脱upx

其实已经是大一下刚开始的事情了,补个档 手动脱壳の新年快乐 查壳,有壳,UPXX32dbg打开文件,查看初始断点点击PUSHAD跟进,CTRL+*设置EIP,开始F8步过,寻找ESP寄存器第一次单个变红的地址此时的内存窗口开始步过第一次步过就发现ESP单个变红,右键跟进内存窗口然后在第一个…

使用firemin降低火狐内存占用

这些年一直使用火狐浏览器,之前一直在AMD平台的机器使用,没有遇到过内存占用过大的问题(可能也与平台无关)。现在在Intel CPU的机器上使用,时间一久,内存就占用很大。试过Firefox/内存消耗严重里面的办法,效果不明显。也试过修改about:config里面的一些选项,也没有达到…

代码随想录算法 - 回溯算法1

题目1 77. 组合 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1: 输入:n = 4, k = 2 输出: [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4], ]示例 2: 输入:n = 1, k = 1 输出:[[1]]提示:1 <= n <= 20 1 <= k…