[斜率优化][计算几何]JZOJ 3238 超空间旅行
分析
又要抠最短路出来
不难发现方程式是个直线表达式(而且有x环最多跑一边否则不优)
那么两条直线的交点算作特殊点,我们要维护一个有上界的上凸壳
最短路种类数即为最后一条直线与上界的交点的横坐标下取整
总长度只是等差数列,还挺好求的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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:; } }
在日渐沉没的世界里,我发现了你。