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;
}