[斜率优化][计算几何]JZOJ 3238 超空间旅行

Description

在遥远的未来,行星之间的食品运输将依靠单向的贸易路线。每条路径直接连接两个行星,且其运输时间是已知的。

贸易商协会打算利用一项最近发现的新技术——超空间旅行,以增加一些新的航线。通过超空间旅行的航线也是单向的。由于该项技术仍处于试验阶段,超空间旅行的时间目前是未知的,但它不取决于行星之间的距离,所以每个超空间旅行的路线将花费等量的时间。

下图是三个相互联通的行星及其运输时间的例子。行星使用正整数标号,超空间旅行时间记为“x”(图片对应第二个输入样例):



运输时间以天计,并且始终是一个正整数。

贸易商协会希望对引进新航线的结果进行分析:对于某两个行星A和B,他们想知道对于任意的x,从A到B的最短路径的总运输时间的所有可能的值。例如,在上述情况中,从星球2到星球1的最短路径所需时间可以取值5(如果x≥5),4,3,2,或1天(如果x<5)

 
 

Input

输入的第一行包含两个整数P和R,分别代表行星的数目和航线数量,1≤P≤500,0≤R≤10000。

接下来的R条航线路径包含两或三个整数:行星标号C和D(1≤C,D≤P,C≠D),和T,从C到D的旅行时间。对于传统的路径,T是一个整数(1≤T≤1000000),超空间航线中,T是字符“x”。 可以存在多行有两个相同的行星。

下一行输入整数Q(1≤Q≤10),表示查询的数量。

以下Q行包含两个整数星球标号(A和B,A≠B),为贸易商协会的查询:“从A到B的最短路径时间的可能值是什么?

Output

 输出必须包含Q行。

 每一行都必须包含两个整数:不同的可能值的数目和它们的总和。如果不同的可能值的数目是无限的,该行只输出“inf”。如果不存在从A到B的路径,不同的可能值的数目及它们的总和都是0。
 

Sample Input

输入1:
4 4
1 2 x
2 3 x
3 4 x
1 4 8
3
2 1
1 3
1 4

输入2:
3 5
3 2 x
2 1 x
2 1 5
1 3 10
3 1 20
6
1 2
2 3
3 1
2 1
3 2
1 3
 

Sample Output

输出1:
0 0
inf
3 17

输出2:
inf
5 65
15 185
5 15
inf
1 10
 
 

Data Constraint

50%的数据满足P≤30,R≤300,T≤50。

如果一个询问的结果不是“inf”,则你的输出必须输出两个数,否则不得分;如果所有询问的第一问正确可以得一半分。

 
 

Hint

样例 1解释:

1. 从2到1不存在可能的路径。

2. 对于任意正整数x,从1到3的最短路径花费的时间是2x,所以答案是“inf”。

3.  从1到4的最短路径可以花费3(当x=1),6(当x=2),或8(当x≥3)的时间,3+6+8=17

分析

又要抠最短路出来

不难发现方程式是个直线表达式(而且有x环最多跑一边否则不优)

那么两条直线的交点算作特殊点,我们要维护一个有上界的上凸壳

最短路种类数即为最后一条直线与上界的交点的横坐标下取整

总长度只是等差数列,还挺好求的

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=510;
const ll Inf=1ll<<62;
const double eps=1e-7;
struct Dij {
    int u,cnt;
    ll dis;
    friend bool operator > (Dij a,Dij b) {
        return a.dis>b.dis;
    }
};
struct Graph {
    int v,w,nx;
}g[20*N];
int cnt,list[N];
ll dis[N][N],ans,sum,r;
bool vis[N][N];
int stk[N],top;
int n,m,Q,s,t;
priority_queue<Dij,vector<Dij>,greater<Dij> > q;

inline void Add(int u,int v,int w) {
    g[++cnt]=(Graph){v,w,list[u]};list[u]=cnt;
}

inline void Dijkstra() {
    while (!q.empty()) q.pop();
    for (register int i=1;i<=n;i++)
        for (register int j=0;j<=n;j++) dis[i][j]=Inf,vis[i][j]=0;
    q.push((Dij){s,0,0});dis[s][0]=0;
    while (!q.empty()) {
        register int u=q.top().u,dist=q.top().dis,c=q.top().cnt;q.pop();
        if (vis[u][c]) continue;m=max(m,c);vis[u][c]=1;
        for (int i=list[u];i;i=g[i].nx)
            if (c+(g[i].w==0)<n&&dis[g[i].v][c+(g[i].w==0)]>dis[u][c]+g[i].w)
                dis[g[i].v][c+(g[i].w==0)]=dis[u][c]+g[i].w,
                q.push((Dij){g[i].v,c+(g[i].w==0),dis[g[i].v][c+(g[i].w==0)]});
    }
}

inline double Slope(int x,int y) {return 1.0*(dis[t][y]-dis[t][x])/(x-y);}

inline ll Linecut(int x,int y) {
    ll X=Slope(x,y);
    if (Slope(x,y)-1.0*X<=eps) X-=0.5;
    return (int)X;
}

int main() {
    scanf("%d%d",&n,&m);
    for (register int i=1,u,v,w;i<=m;i++) {
        char c[10];memset(c,0,sizeof c);
        scanf("%d%d%s",&u,&v,c);
        w=0;
        if (c[0]!='x') {
            int len=strlen(c);
            for (int j=0;j<len;j++) w=w*10+c[j]-48;
        }
        Add(u,v,w);
    }
    for (scanf("%d",&Q);Q;Q--) {
        scanf("%d%d",&s,&t);m=0;
        Dijkstra();
        for (register int i=0;i<=m;i++) if (dis[t][i]<Inf) break; else
        if (i==m) {
            printf("0 0\n");
            goto end;
        }
        if (dis[t][0]==Inf) {
            printf("inf\n");
            goto end;
        }
        top=0;stk[++top]=0;
        for (register int i=1;i<=m;i++)
            if (dis[t][i]<Inf) {
                while (top>1&&Slope(stk[top],stk[top-1])<=Slope(i,stk[top])) top--;
                if (Linecut(i,stk[top])>0)stk[++top]=i;
            }
        r=sum=0;ans=1;
        for (register int i=top;i>1;i--) {
            r=Linecut(stk[i-1],stk[i]);
            sum+=(r-ans+1)*dis[t][stk[i]]+(ans+r)*(r-ans+1)/2*stk[i];
            ans=r+1;
        }
        printf("%lld %lld\n",ans,sum+dis[t][0]);
        end:;
    }
}
View Code

 

posted @ 2019-07-12 20:59  Vagari  阅读(248)  评论(0编辑  收藏  举报