题解:
不难发现,对于每一条从$S$到$T$的路径,设其$x、y$的和为$S_x、S_y$,其对答案的贡献是$acdot S_x+(1-a)cdot S_y$,这是一个关于$a$的一次函数。而所有的路径就对应着许多$ain [0,1]$直线,而不同$a$所对应的最短路长度恰好构成了这些直线的上凸壳,而求最短路的期望就是求上凸壳的积分(与坐标系横轴所夹面积),考虑到$n、m$很小,所以直接用一次函数不断拟合,每次求点值暴力跑一边最短路即可。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #define LL long long #define M 802 #define N 202 #define INF 1010000000 #define eps (1e-5) using namespace std; int read(){ int nm=0,fh=1; char cw=getchar(); for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0'); return nm*fh; } struct STA{ int nd; double dst; STA(); STA(int _nd,int _dst){nd=_nd,dst=_dst;} bool operator <(const STA&ot)const{return dst>ot.dst;} }; priority_queue<STA> Q; int n,m,X[M],Y[M],fs[N],nt[M],to[M],tmp,S,T; double dis[N]; bool vis[N]; void link(int x,int y,int t1,int t2){nt[tmp]=fs[x],fs[x]=tmp,to[tmp]=y,X[tmp]=t1,Y[tmp++]=t2;} double DJ(double V){ for(int i=1;i<=n;i++) dis[i]=INF*1.0,vis[i]=false; while(!Q.empty()) Q.pop(); dis[S]=0.0,Q.push(STA(S,0.0)); while(!Q.empty()){ int x=Q.top().nd; Q.pop(); if(vis[x]) continue; vis[x]=true; for(int i=fs[x];i!=-1;i=nt[i]){ double dt=dis[x]+(X[i]*V)+(Y[i]*(1.0-V)); if(dt>=dis[to[i]]) continue; dis[to[i]]=dt,Q.push(STA(to[i],dt)); } } return dis[T]; } double calc(double L,double R){ double mid=(L+R)/2.0,ans,ls,rs; ans=DJ(mid),ls=DJ(L),rs=DJ(R); if(fabs(ans-(ls+rs)/2.0)*(R-L)<eps) return ans*(R-L); return calc(L,mid)+calc(mid,R); } int main(){ n=read(),m=read(),S=read(),T=read(),memset(fs,-1,sizeof(fs)); for(int i=1;i<=m;i++){ int u=read(),v=read(),t1=read(),t2=read(); link(u,v,t1,t2),link(v,u,t1,t2); } printf("%.10f ",calc(0.0,1.0)); return 0; }