【BZOJ3482】【JZOJ3238】[COCI2013]hiperprostor 超空间旅行
题目大意
题目链接
给出一个p个点r条边的有向图,某些边长度给定,某些边长度是一个未知的正整数x(都是x)。有q个询问,每次询问s到t的最短路长度有几种可能,以及这些可能的长度之和。
分析
可以发现任意一条s到t的最短路都能表示成kx+b的形式;那么我们可以设f[i][j]表示由s走到i,k=j时b的最小值,用最短路算法求出f数组。那么此时s到t的最短路就可以用若干个一次函数kx+f[t][k]表示了。
把这些一次函数变成直线放在坐标系,考虑两条直线,在交点两侧两条直线分别更优。于是我们可以考虑把这些直线的上凸壳做出来,显然相邻两条直线的交点横坐标是递增的,这些交点的横坐标把x轴分成若干段,我们分别计算每一段的贡献即可。
注意判掉一些不合法的情况。注意常数优化。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
typedef long long ll;
const int N = 507, M = 10007;
char ch;
int n, m, q, s, t;
int tot, st[N], to[M], nx[M], len[M];
void add(int u, int v, int w) { to[++tot] = v, nx[tot] = st[u], len[tot] = w, st[u] = tot; }
int head, tail, que[N * N * 14][2], vis[N][N];
ll ans1, ans2, f[N][N];
void spfa()
{
memset(f, 0x3f, sizeof(f));
memset(vis, 0, sizeof(vis));
head = 1, que[tail = 1][0] = s, que[tail][1] = 0, f[s][0] = 0, vis[s][0] = 1;
while (head <= tail)
{
int u = que[head][0], p = que[head][1]; head++;
vis[u][p] = 0;
for (register int i = st[u], v, j, t; i; i = nx[i])
{
v = to[i], j = p + (len[i] == -1);
if (j > n - 1) continue;
t = f[u][p] + (len[i] == -1 ? 0 : len[i]);
if (t < f[v][j])
{
f[v][j] = t;
if (!vis[v][j]) que[++tail][0] = v, que[tail][1] = j, vis[v][j] = 1;
}
}
}
}
double inter(int k0, ll b0, int k1, ll b1) { return (b1 - b0) * 1.0 / (k0 - k1); }
int top;
ll stk[N][2];
void solve()
{
int flag = 1; for (int i = 0; i <= n - 1; i++) if (f[t][i] < f[506][506]) { flag = 0; break; }
if (flag) { printf("0 0\n"); return; }
flag = 1; for (int i = 1; i <= n - 1; i++) if (f[t][i] < f[506][506]) { flag = 0; break; }
if (!flag && f[t][0] >= f[506][506]) { printf("inf\n"); return; }
ans1 = ans2 = top = 0;
for (int i = n - 1; i >= 0; i--)
{
if (f[t][i] > f[t][0]) continue;
if (!top) { stk[++top][0] = i, stk[top][1] = f[t][i]; continue; }
while ((top > 0 && f[t][i] < stk[top][1]) || (top > 1 && inter(stk[top - 1][0], stk[top - 1][1], i, f[t][i]) <= inter(stk[top - 1][0], stk[top - 1][1], stk[top][0], stk[top][1]))) top--;
stk[++top][0] = i, stk[top][1] = f[t][i];
}
ll l, r;
for (int i = 1; i <= top; i++)
{
if (i == 1) l = 1;
else l = r + 1;
if (i == top) r = l;
else r = inter(stk[i][0], stk[i][1], stk[i + 1][0], stk[i + 1][1]);
if (inter(stk[i][0], stk[i][1], stk[i + 1][0], stk[i + 1][1]) == r) r--;
ans1 += r - l + 1, ans2 += (stk[i][0] * l + stk[i][1] + stk[i][0] * r + stk[i][1]) * (r - l + 1) / 2;
}
printf("%lld %lld\n", ans1, ans2);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1, a = 0, b = 0, c; i <= m; i++)
{
scanf("%d%d", &a, &b), c = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar()) if (ch == 'x') { c = -1; break; }
if (c != -1) for (; ch >= '0' && ch <= '9'; ch = getchar()) c = c * 10 + ch - '0';
add(a, b, c);
}
scanf("%d", &q);
while (q--)
{
scanf("%d%d", &s, &t);
spfa(), solve();
}
return 0;
}
作者:zjlcnblogs
出处:https://www.cnblogs.com/zjlcnblogs/p/11173226.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
分类:
OI
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· “你见过凌晨四点的洛杉矶吗?”--《我们为什么要睡觉》
· 编程神器Trae:当我用上后,才知道自己的创造力被低估了多少
· 开发的设计和重构,为开发效率服务
· 从零开始开发一个 MCP Server!
· Ai满嘴顺口溜,想考研?浪费我几个小时