题意
一张图,上面有些边可能为
x
x
x,
x
x
x不是一个固定的值。每次询问给出
A
A
A和
B
B
B,求出最短路不同的可能值的数目和它们的总和。如果不同的可能值的数目是无限的,该行只输出
i
n
f
inf
inf。如果没有从
A
A
A到
B
B
B的路径,不同的可能值的数目及它们的总和都是
0
0
0。
。
思路
先分层图跑出
f
i
f_i
fi代表经过
i
i
i条
x
x
x边的最短路。
对于第一问,判断一下求出的
f
f
f就好了。
对于第二问:
经过
i
i
i条
x
x
x边代价为
f
i
+
i
∗
x
f_i+i*x
fi+i∗x
把
y
=
f
i
+
i
∗
x
y=f_i+i*x
y=fi+i∗x扔到坐标系,可以发现当
x
x
x不同时,到最短路经过的
x
x
x边数也不同,由于是最短路,所以我们在坐标系中对每个
x
x
x取
y
y
y最小的情况,发现是个上凸壳,单调栈维护一下,对凸壳中两个交点,答案贡献为
f
[
i
]
+
i
x
(
x
f[i]+ix(x
f[i]+ix(x从一个点到另一个点
)
)
),用等差数列搞一搞。
代码
#include<queue>
#include<cstdio>
#include<cstring>
const int inf = 707406378;
int n, m, tot, s, t;
int dis[501][501], vis[501][501], stack[501];
int ver[10001], next[10001], edge[10001], head[501];
double p[501];
struct heap{
int u, dis, used;
};
bool operator <(const heap &a, const heap &b) {
return a.dis > b.dis;
}
int read() {
int res = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f *= -1;
if (ch == 'x') return 0;
ch = getchar();
}
while (ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0', ch = getchar();
return res;
}
void add(int u, int v, int w) {
ver[++tot] = v;
next[tot] = head[u];
edge[tot] = w;
head[u] = tot;
}
void dijkstra() {
std::priority_queue<heap> q;
memset(dis, 127 / 3, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[s][0] = 0;
q.push((heap){s, 0, 0});
while (q.size()) {
int u = q.top().u, rk = q.top().used;
q.pop();
if (vis[u][rk]) continue;
vis[u][rk] = 1;
for (int i = head[u]; i; i = next[i]) {
int v = ver[i];
if (rk < n && !vis[v][rk + 1] && dis[v][rk + 1] > dis[u][rk] && !edge[i]) dis[v][rk + 1] = dis[u][rk], q.push((heap){v, dis[v][rk + 1], rk + 1});
if (!vis[v][rk] && dis[v][rk] > dis[u][rk] + edge[i] && edge[i]) dis[v][rk] = dis[u][rk] + edge[i], q.push((heap){v, dis[v][rk], rk});
}
}
}
double get(int x, int y) {
return (double)(dis[t][x] - dis[t][y]) / (y - x);
}
long long add(int k, int b, int l, int r) {
return (long long)(k * (l + r) + b * 2) * (r - l + 1) >> 1;
}
int main() {
n = read();
m = read();
for (int i = 1; i <= m; i++) {
int x = read(), y = read(), z = read();
add(x, y, z);
}
for (int q = read(); q; q--) {
s = read();
t = read();
dijkstra();
int flag = 0;
for (int i = 0; i <= n; i++)
if (dis[t][i] != inf) {
flag = 1;
break;
}
if (!flag) {
printf("0 0\n");
continue;
}
if (dis[t][0] == inf) {
printf("inf\n");
continue;
}
int top = 0;
for (int i = n; i >= 0; i--) {
if (dis[t][i] == inf) continue;
while (top && p[top] >= get(stack[top], i)) top--;
if (top) p[top + 1] = get(stack[top], i);
stack[++top] = i;
}
int sum = p[top];
long long ans = 0;
for (int i = 1; i < top; i++) ans += add(stack[i], dis[t][stack[i]], (int)p[i] + 1, (int)p[i + 1]);
if (sum * stack[top - 1] + dis[t][stack[top - 1]] != dis[t][0] || top == 1) ans += dis[t][0], sum++;
printf("%d %lld\n", sum, ans);
}
}