幻想鄉三連C——狂飆突進的幻想鄉
阿新 • • 發佈:2018-09-15
const ring char turn size cst read getc pac
題解:
不難發現,對於每一條從$S$到$T$的路徑,設其$x、y$的和為$S_x、S_y$,其對答案的貢獻是$a\cdot S_x+(1-a)\cdot S_y$,這是一個關於$a$的一次函數。而所有的路徑就對應著許多$a\in [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\n",calc(0.0,1.0)); return 0; }
幻想鄉三連C——狂飆突進的幻想鄉