一看题面就是高斯消元解 $dp$ 方程组,设 $f[x]$ 表示从起点到终点,经过节点 $x$ 的期望次数
那么对于一个点 $x$,枚举所有相连的边 $(x,v)$ ,其中 $v eq n$,设节点 $v$ 的出度为 $du[v]$ ,那么有
$f[x]=sum_{v}frac {f[v]} {du[v]}$
然后对于 $n$ 个点都有一个方程,直接解方程组即可
然后发现题目有点意思,问的是给边安排权值使得得到的权值期望最小,冷静分析一波,刚刚高斯消元解出来的东西肯定有用
如果我们能确定每条边被经过的期望次数,那么直接按期望从小到大给从大到小的权值即可
发现对于一条边,它被经过的期望次数其实和它连接的两点有关,具体地:
设边为 $(u,v)$ ,那么它被经过的期望次数就是 $frac {f[u]} {du[u]}+frac {f[v]} {du[v]}$
所以这题就解决了
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; typedef double db; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=507,M=N*N; int n,m,du[N]; int fir[N],from[M],to[M],cntt; inline void add(int a,int b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; } db A[N][N],ans[N],p[M]; void Gauss() { for(int i=1;i<=n;i++) { int pos=i; for(int j=i+1;j<=n;j++) if(fabs(A[j][i])>fabs(A[pos][i])) pos=j; swap(A[i],A[pos]); for(int j=i+1;j<=n;j++) { db t=A[j][i]/A[i][i]; for(int k=i;k<=n+1;k++) A[j][k]-=t*A[i][k]; } } for(int i=n;i>=1;i--) { for(int j=i+1;j<=n;j++) A[i][n+1]-=A[i][j]*ans[j]; ans[i]=A[i][n+1]/A[i][i]; } } int d[M][2]; int main() { n=read(),m=read(); for(int i=1;i<=m;i++) { d[i][0]=read(),d[i][1]=read(); add(d[i][0],d[i][1]),add(d[i][1],d[i][0]); du[d[i][0]]++,du[d[i][1]]++; } for(int i=1;i<=n;i++) A[i][i]=1; for(int i=1;i<n;i++) for(int j=fir[i];j;j=from[j]) { int &v=to[j]; A[v][i]-=1.0/du[i]; } A[1][n+1]=1; Gauss(); db sum=0; for(int i=1;i<=m;i++) { if(d[i][0]!=n) p[i]+=ans[d[i][0]]/du[d[i][0]]; if(d[i][1]!=n) p[i]+=ans[d[i][1]]/du[d[i][1]]; } sort(p+1,p+m+1); reverse(p+1,p+m+1); for(int i=1;i<=m;i++) sum+=p[i]*i; printf("%.3lf ",sum); return 0; }