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