3238. 超空间旅行
Description
在遥远的未来,行星之间的食品运输将依靠单向的贸易路线。每条路径直接连接两个行星,且其运输时间是已知的。
贸易商协会打算利用一项最近发现的新技术——超空间旅行,以增加一些新的航线。通过超空间旅行的航线也是单向的。由于该项技术仍处于试验阶段,超空间旅行的时间目前是未知的,但它不取决于行星之间的距离,所以每个超空间旅行的路线将花费等量的时间。
下图是三个相互联通的行星及其运输时间的例子。行星使用正整数标号,超空间旅行时间记为“x”(图片对应第二个输入样例):
运输时间以天计,并且始终是一个正整数。
贸易商协会希望对引进新航线的结果进行分析:对于某两个行星A和B,他们想知道对于任意的x,从A到B的最短路径的总运输时间的所有可能的值。例如,在上述情况中,从星球2到星球1的最短路径所需时间可以取值5(如果x≥5),4,3,2,或1天(如果x<5)
Solution
可以发现,最后的最短路一定是多条形如 \(ax+b\) 这样的式子。这跟一次函数的解析式很像,而最短路也就是求出这些直线每个时刻的最小值。画图发现求的是上凸壳。
根据思路,先 \(\text{spfa}\) 求出 \(dis_{i,j}\),表示走了 \(j\) 条 x 边,到达点 \(i\) 的最短路。而 \(dis_{i,j}\) 本身就是直线的截距。
单调队列维护上凸壳。
计算答案时,将相邻两直线的交点求出,用等差数列计算即可。为了避免重复,可将截距减 1。
Code
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 505
#define M 10005
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
int n,m,x,y,z,tot,q,cnt,dis[N][N],sta[N];
ll ans1,ans2;
bool flag,bj[N][N];
char ch;
struct node {int to,next,head,val,typ;} a[M];
struct mindis {int pos,num;};
queue<mindis> Q;
int read()
{
int res=0,fh=1;char ch=getchar();
while (ch<'0'||ch>'9'&&ch!='x') {if (ch=='-') fh=-1;ch=getchar();}
if (ch=='x') return 0;
while (ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch-'0'),ch=getchar();
return res*fh;
}
void add(int x,int y,int z)
{
a[++tot].to=y;a[tot].val=z;
a[tot].next=a[x].head;a[x].head=tot;
}
void spfa(int st,int ed)
{
while (!Q.empty()) Q.pop();
memset(dis,inf,sizeof(dis));
memset(bj,false,sizeof(bj));
Q.push((mindis){st,0});dis[st][0]=0;bj[st][0]=true;
while (!Q.empty())
{
int x=Q.front().pos,y=Q.front().num;Q.pop();
if (y>=n) continue;
bj[x][y]=true;
for (int i=a[x].head;i;i=a[i].next)
{
if (dis[a[i].to][y+(a[i].val==0)]>dis[x][y]+a[i].val)
{
dis[a[i].to][y+(a[i].val==0)]=dis[x][y]+a[i].val;
if (!bj[a[i].to][y+(a[i].val==0)]) Q.push((mindis){a[i].to,y+(a[i].val==0)}),bj[a[i].to][y+(a[i].val==0)]=true;
}
}
bj[x][y]=false;
}
}
double calc(int x,int y,int ed) {return 1.0*(dis[ed][x]-dis[ed][y])/(y-x);}
int main()
{
freopen("T1.in","r",stdin);
freopen("T1.out","w",stdout);
n=read();m=read();
for (int i=1;i<=m;++i)
{
x=read();y=read();z=read();
add(x,y,z);
}
scanf("%d",&q);
while (q--)
{
x=read();y=read();
spfa(x,y);
bool flag=false;
for (int i=0;i<=n;++i)
flag|=(dis[y][i]<inf);
if (!flag) {printf("0 0\n");continue;}
if (dis[y][0]>=inf) {printf("inf\n");continue;}
sta[cnt=1]=0;
for (int i=1;i<=n;++i)
if (dis[y][i]<dis[y][sta[cnt]])
{
while (calc(sta[cnt],sta[cnt-1],y)<calc(i,sta[cnt],y)&&cnt>1) --cnt;
sta[++cnt]=i;
}
ans1=1;ans2=dis[y][0];
for (int i=cnt,lst=0;i>1;--i)
{
x=((dis[y][sta[i-1]]-dis[y][sta[i]]-1+0.0)/(sta[i]-sta[i-1]));
ans2+=((ll)sta[i]*(x+lst+1)+(ll)2*dis[y][sta[i]])*(ll)(x-lst)/2;
ans1=x+1;lst=x;
}
printf("%lld %lld\n",ans1,ans2);
}
return 0;
}