题意
给定一个 n个点 m 条边的无向连通图,顶点从 1 编号到 n,边从 1编号到 m。
小 Z 在该图上进行随机游走,初始时小 Z 在 1号顶点,每一步小 Z 以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小 Z 到达 n 号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这 m 条边进行编号,使得小 Z 获得的总分的期望值最小。
思路
首先,一个较为显然的性质, 获得的总分的期望值 就是 每一条边期望经过的次数 乘上 其权值
然后贪心地将小权值赋给期望次数大的边
但边数实际上最大会有(125000),会跑不过,我们此时运用技巧点边转换,考虑点的期望经过次数与边的关系,得到 边的期望经过次数 = $$f_u frac{1}{deg_v} + f_v frac{1}{deg_v}$$
其中(deg)为点的度数,(u,v)分别为一条边的两个端点,(f)为点的期望经过次数
然后考虑点,对于点n,它不会被经过,因为到它就停了,于是有以下式子
[f_i=sum_{e(j,i)in E,j
eq n} frac{f_j}{d_j}
]
注意,当(i=1)时,需特判为(f_1=sum_{e(j,1)in E,j eq n} frac{f_j}{d_j}+1),因为从1开始.
然后就发现不能递推QAQ
但是这可以转换一个n-1元1次方程组,于是高斯消元
代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int d[510];
double a[510][510],c[510],val[125010],x[510];
double ans=0.0;
struct edge{
int x,y;
}e[125010];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&e[i].x,&e[i].y);
d[e[i].x]++,d[e[i].y]++;
}
for(int i=1;i<n;i++)a[i][i]=1.0,c[i]=0.0;
for(int i=1;i<=m;i++){
if(e[i].x==n || e[i].y==n)continue;
a[e[i].x][e[i].y]=-1.0/d[e[i].y];
a[e[i].y][e[i].x]=-1.0/d[e[i].x];
}
c[1]=1.0;
for(int i=1;i<=n-1;i++){
int maxx=i;
for(int j=i+1;j<=n-1;j++)
if(fabs(a[maxx][i])<fabs(a[j][i]))maxx=j;
if(maxx!=i)swap(a[i],a[maxx]),swap(c[i],c[maxx]);
for(int j=1;j<=n-1;j++){
if(j==i)continue;
double rate=a[j][i]/a[i][i];
for(int k=i;k<=n-1;k++)a[j][k]-=rate*a[i][k];
c[j]-=rate*c[i];
}
}
for(int i=1;i<=n-1;i++)x[i]=c[i]/a[i][i];
for(int i=1;i<=m;i++){
if(e[i].x!=n)val[i]+=1.0*x[e[i].x]/d[e[i].x];
if(e[i].y!=n)val[i]+=1.0*x[e[i].y]/d[e[i].y];
}
sort(val+1,val+m+1);
for(int i=1;i<=m;i++){
ans+=(m-i+1)*val[i];
}
printf("%.3lf
",ans);
return 0;
}