1. 程式人生 > >幻想鄉三連C——狂飆突進的幻想鄉

幻想鄉三連C——狂飆突進的幻想鄉

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——狂飆突進的幻想鄉