题目描述
开学了,小奇在回地球的路上,遇到了一个棘手的问题。
简单来说,它要从标号为 (1) 的星球到标号为 (n) 的星球,某一些星球之间有航线。由于超时空隧道的存在,从一个星球到另一个星球时间可能会倒流,而且,从星球 (a) 到 (b) 耗费的时间和星球 (b) 到 (a) 耗费的时间不一定相同。
宇宙法规定:“禁止在出发时间前到达目的地。”
每艘飞船上都有速度调节装置,可以调节飞行的时间。其功能可以使得整次航程中所有两星球间的飞行时间增加或减少相同的整数值。你的任务是帮助它调整速度调节器,使得在某个值下,从 (1) 号星球到 (n) 号星球有确定的最短路线和距离,并最小化这个距离。注意这个距离必须不小于 (0)。
输入格式
输入文件包含多组数据 ,第 (1) 个数为 (T) ,表示数据组数。
对于每组数据,输入第 (1) 行为两个正整数 (n),(m),为星球的个数和星球间的路线数。接下来 (m) 行,每行三个整数 (i),(j) 和 (t),表示由星球 (i) 到星球 (j) 飞行的时间为 (t) 。由 (i) 到 (j) 最多只会有一条飞行线路。
输出格式
输出文件共 (T) 行,每组数据输出一行。
如果可以通过调节速度调节器完成任务,则输出一个非负整数,表示由星球 (1) 到星球 (n) 的最短时间。(注意最短时间要大于或者等于 (0))。
如果不能由星球 (1) 到达星球 (n),则输出 -1
。
数据范围
测试时间限制 (1000 extrm{ms}),空间限制 (256 extrm{MiB})。
- 对于 (20\%) 的数据,保证所有星球出度不超过 (1);
- 对于另外 (20\%) 的数据,(nle 10);
- 对于另外 (20\%) 的数据,(-100le tle 100);
- 对于 (100\%) 的数据,(Tle 10),(nle 100),(mle n(n-1)),(-10^5le tle 10^5)。
数据随机和构造结合生成。
分析
大意是说,给定一张图,要确定一个数,使得每条边加上这个数以后,从 (1) 号节点到 (n) 号节点之间的路径上就没有负环且 (1) 号节点到 (n) 号节点的最短路长度不为负。求出此时 (1) 号节点到 (n) 号节点的最短路。
假定每条边要加上 (k),如何判断此时的 (k) 是否合法和求出此时的最短路呢?
最短路可以通过 SPFA 求解(注意到此时可能有负权边),而有没有负环可以通过深搜版 SPFA 求解,最短路长度是否为负可以求出最短路以后再去算。
复杂度上界 (Theta(Tmax t_inm)),估算一下差不多是 (10^{12}),不够。
那么如何优化呢?考虑枚举 (k) 的过程,若 (k_0) 满足条件,那么 (forall k_1 > k_0) 都是满足条件的,且易证 (k_1) 的答案没有 (k_0) 优。
所以,我们可以通过二分答案来找到这个临界值。复杂度上限降到 (Theta(Tlogmax t_inm)),约为 (4 imes 10^8),很悬,实测 (50)~(60) 分左右。
Code
蒟蒻实在是想不出什么优化了,只能先把辣鸡代码放在这里了。
#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
typedef long long ll;
constexpr int max_n = 100, INF = 0x3f3f3f3f;
ll dis[max_n];
int g[max_n][max_n], n;
bool is_ins[max_n] = {}, vis[2][max_n];
inline int read()
{
int ch = getchar(), n = 0, t = 1;
while (isspace(ch)) { ch = getchar(); }
if (ch == '-') { t = -1, ch = getchar(); }
while (isdigit(ch)) { n = n * 10 + ch - '0', ch = getchar(); }
return n * t;
}
int dm(int x, int y, bool dir)
{
if (dir)
return y;
return x;
}
void dfs(int id, bool dir)
{
for (int i = 0; i < n; i++)
if (g[dm(id,i,dir)][dm(i,id,dir)] != INF && !vis[dir][i])
{
vis[dir][i] = true;
dfs(i, dir);
}
}
bool spfa(int id, int adds)
{
is_ins[id] = true;
for (int i = 0; i < n; i++)
if (g[id][i] != INF)
{
if (is_ins[i] && dis[i] > dis[id] + g[id][i] + adds)
{
is_ins[id] = false;
if (vis[0][i])
return true;
else
return false;
}
else if (dis[i] > dis[id] + g[id][i] + adds)
{
dis[i] = dis[id] + g[id][i] + adds;
if (spfa(i, adds))
{
is_ins[id] = false;
return true;
}
}
}
is_ins[id] = false;
return false;
}
int check_ans(int lim)
{
memset(dis, 0x3f, sizeof(dis));
dis[0] = 0;
if (spfa(0, lim) || dis[n-1] < 0)
return -1;
return dis[n-1];
}
int main()
{
int cas = read(), m, pa, pb, pc, lb, ub, mid;
ll ans;
while (cas--)
{
memset(g, 0x3f, sizeof(g));
memset(vis, false, sizeof(vis));
n = read(), m = read(), lb = 0, ub = 0;
for (int i = 0; i < m; i++)
{
pa = read(), pb = read(), pc = read();
g[pa-1][pb-1] = pc;
if (pc < ub)
ub = pc;
if (pc > lb)
lb = pc;
}
vis[0][0] = vis[1][n-1] = true;
dfs(0, false);
dfs(n - 1, true);
for (int i = 0; i < n; i++)
vis[0][i] &= vis[1][i];
if (!vis[1][0])
{
puts("-1");
continue;
}
lb = -lb, ub = -ub + 1, ans = -1;
while (lb < ub)
{
mid = (lb + ub) >> 1;
pa = check_ans(mid);
if (pa == -1)
lb = mid + 1;
else
ans = pa, ub = mid;
}
printf("%lld
", ans);
}
return 0;
}