Description
给n个点,m条边的无向图,有两种边,一种是x边,x的值由你来选择,一种是普通边给出权值。给10组询问每次询问x~y最短路的可能性有多少条,并求出它们的和。
Sample Input
4 4
1 2 x
2 3 x
3 4 x
1 4 8
3
2 1
1 3
1 4
Sample Output
0 0
inf
3 17
10组询问单组来做。
首先,可以想到做一个DP。
f[i][j]表示到达i这个点,经过j条x边的最短路。
然后呢,你想假设你选择一条经过j个x边的路径,他的长度就是f[ed][j]+j*x
然后这是条直线,你就求个凸包即可。
答案统计什么的好像有点细节啊,挺麻烦的。。。
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
struct node {
int x, k, d;
friend bool operator < (node a, node b) {return a.d > b.d;}
}; priority_queue<node> q;
struct edge {
int x, y, c, next, id;
} e[11000]; int len, last[11000];
int yp, st, ed, sta[510], b[510];
int n; bool v[510][510]; LL f[510][510];
char ss[10];
void ins(int x, int y, int c, int id) {
e[++len].x = x; e[len].y = y; e[len].c = c; e[len].id = id;
e[len].next = last[x]; last[x] = len;
}
void dij() {
memset(f, 63, sizeof(f));
memset(v, 0, sizeof(v));
f[st][0] = 0;
node tmp; tmp.x = st, tmp.d = 0, tmp.k = 0;
q.push(tmp);
while(!q.empty()) {
node now = q.top(); q.pop();
int x = now.x, i = now.k;
if(v[x][i]) continue; v[x][i] = 1;
for(int k = last[x]; k; k = e[k].next) {
int y = e[k].y;
int o = i + e[k].id; if(o > n) continue;
if(f[y][o] > f[x][i] + e[k].c) {
f[y][o] = f[x][i] + e[k].c;
if(!v[y][o]) tmp.x = y, tmp.d = f[y][o], tmp.k = o, q.push(tmp);
}
}
}
}
double lv(int x, int y) {
return (double)(f[ed][y] - f[ed][x]) / (x - y);
}
int cs(int x, int y) {
double hh = f[ed][y] - f[ed][x];
if(hh <= 0) return 0;
return (hh - 1) / (x - y);
}
int main() {
int m; scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++) {
int x, y; scanf("%d%d", &x, &y);
scanf("%s", ss + 1);
if(ss[1] == 'x') ins(x, y, 0, 1);
else {
int s = 0;
for(int j = 1; j <= strlen(ss + 1); j++) s = s * 10 + ss[j] - '0';
ins(x, y, s, 0);
}
}
int q; scanf("%d", &q);
while(q--) {
st, ed; scanf("%d%d", &st, &ed);
dij(); bool bk = 0;
for(int i = 0; i < n; i++) if(f[ed][i] < 999999999) bk = 1;
if(!bk) {puts("0 0"); continue;}
if(f[ed][0] > 999999999) {puts("inf"); continue;}
int tp = 0;
for(int i = n - 1; i >= 0; i--) if(f[ed][i] < 999999999){
while(tp > 1 && lv(sta[tp - 1], sta[tp]) >= lv(sta[tp], i)) tp--;
sta[++tp] = i;
} LL ans1 = 1, ans2 = f[ed][0];
for(int i = 1; i < tp; i++) {
b[i] = cs(sta[i], sta[i + 1]);
int l = b[i - 1] + 1, r = b[i];
if(l < 1) l = 1;
if(r < l) continue;
ans1 += r - l + 1;
ans2 += ((LL)(r + l) * sta[i] + 2LL * f[ed][sta[i]]) * (r - l + 1) / 2;
} printf("%lld %lld\n", ans1, ans2);
}
return 0;
}