Loading

【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;
}
posted @ 2019-07-11 22:29  gz-gary  阅读(165)  评论(0编辑  收藏  举报