传送门:bzoj3143
题解
边 l i l_i li的期望经过次数为 w i w_i wi。
贪心将边按 w w w降序排序,答案即 ∑ i ⋅ w i \sum i·w_i ∑i⋅wi。
但边的期望不好统计,考虑转成点:
设
d
p
i
dp_i
dpi表示点
i
i
i的期望经过次数,
d
i
d_i
di为点的出度,则
w
=
[
u
≠
n
]
d
p
u
d
u
+
[
v
≠
n
]
d
p
v
d
v
w=[u\neq n]\frac{dp_u}{d_{u}}+[v\neq n]\frac{dp_v}{d_v}
w=[u̸=n]dudpu+[v̸=n]dvdpv
对于前 n − 1 n-1 n−1个点,存在环形的DP转移,高斯消元求解即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef double db;
const int N=550;
const db eps=1e-12;
int n,m,g[N][N],tot,to[N];
db a[N][N],p[N],ky[N*N],ans;
inline bool vl(db x)
{return (x<=eps) && (x>=-eps);}
inline void init()
{
int i,j,ix,iy;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i){
scanf("%d%d",&ix,&iy);
to[ix]++;to[iy]++;
g[ix][iy]=g[iy][ix]=1;
}
}
inline void build()
{
int i,j;
a[1][n]=1.0;
for(i=1;i<n;++i){
a[i][i]=1.0;
for(j=1;j<n;++j) if(g[i][j])
a[i][j]=-1.0/(db)to[j];
}
}
inline void gauss()
{
int i,j,k,nw;db bs;
for(i=1;i<n;++i){
if(vl(a[i][i])){
for(j=i+1;j<n;++j)
if(!vl(a[j][i])){nw=j;break;}
for(j=i;j<=n;++j)
swap(a[nw][j],a[i][j]);
}
for(j=i+1;j<n;++j) if(!vl(a[j][i])){
bs=-a[j][i]/a[i][i];
for(k=i;k<=n;++k)
a[j][k]+=bs*a[i][k];
}
}
}
inline void get()
{
int i,j;
for(i=n-1;i;--i){
p[i]=a[i][n]/a[i][i];
for(j=i-1;j;--j)
a[j][n]-=a[j][i]*p[i];
}
for(i=1;i<n;++i)
for(j=i+1;j<=n;++j) if(g[i][j]){
tot++;ky[tot]=p[i]/(db)to[i]+p[j]/(db)to[j];
}
sort(ky+1,ky+tot+1);
for(i=1;i<=m;++i) ans+=ky[i]*(m-i+1.0);
printf("%.3lf\n",ans);
}
int main(){
init();
build();
gauss();
get();
}