P3232 [HNOI2013]游走
Solution
看到题不要慌,不要因为是期望而慌张。像我一样
我们一步一步分析(●'◡'●)
首先,根据贪心的思想,期望经过次数多的边我们给它更小的编号。
那么现在就想怎么求出每条边的期望经过次数?
经过一番思考,发现每条边只与它的两个点及点的度数有关,用式子写一下就是:
(其中 \(e\) 为 \(\langle u,v\rangle\) 这条边, \(g_e\) 表示 \(e\) 的期望经过次数, \(f_u\) 表示 \(u\) 这个点的期望经过次数, \(d_u\) 表示 \(u\) 的度数, \(f_v,d_v\) 同理。)
因为每次经过 \(u\) 的时候,都有 \(\dfrac 1{d_u}\) 的概率经过这条边, \(v\) 同理。
那那么我们就把问题转化到怎么求出每个点的期望经过次数?
又经过一波观察,发现每个点只和它相连的点及点的度数有关,用式子写出来就是:
(其中 \(E_u\) 表示和 \(u\) 相连的点集。)
这个式子的道理和上面和边有关的那个式子是一样的^o^/
但是有两个特殊的点:
1.第 \(1\) 个节点是开始的节点,也就是刚开始就走过了,所以 \(f_1\) 的转移不一样: \(f_1=1+\sum \frac {f_v}{d_v}\) 。
2.第 \(n\) 个节点是结束的节点,也就是走到 \(n\) 即结束,所以任何一个点不能从 \(n\) 转移,计算 \(g\) 的时候也不能将 \(\dfrac {f_n}{d_n}\) 计入答案。最简单的方法就是 \(f_n=0\) 。
接下来我们可以将 \(f\) 的 \(n-1\) 个式子看做 \(n-1\) 个 \(n-1\) 元方程组,用高斯消元求解即可。
注意:因为是方程组,式子的形式都是 \(a_{k,1}*f_1+a_{k,2}*f_2+\cdots+a_{k,i}*f_i+\cdots +a_{k,n}*f_n=f_k\) ( \(a_{k,i}\) 代表 \(f_i\) 在第 \(k\) 个式子中的系数),所以对于特殊的 \(f_1\) ,式子要变成 \(\sum a_{1,i}*f_i=f_1-1\) 。
Code
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=510,M=125010;
const double eps=1e-9;
int n,m,cnt,head[N],d[N],u[M],v[M];
struct edge{
int to,nxt;
}e[M<<1];
double a[N][N],g[M],ans;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
inline void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
e[++cnt].to=u;
e[cnt].nxt=head[v];
head[v]=cnt;
d[u]++; d[v]++;
}
inline void gauss(){
for(int i=1;i<=n;i++){
int max=i;
for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>fabs(a[max][i])) swap(max,j);
if(max!=i)
for(int j=1;j<=n+1;j++) swap(a[i][j],a[max][j]);
if(fabs(a[i][i])<eps) continue;
for(int j=1;j<=n;j++){
if(j!=i){
double tmp=a[j][i]/a[i][i];
for(int k=i+1;k<=n+1;k++)
a[j][k]-=a[i][k]*tmp;
}
}
}
for(int i=1;i<=n;i++)
a[i][n+1]/=a[i][i];
}
int main(){
n=read(); m=read();
for(int i=1;i<=m;i++){
u[i]=read(); v[i]=read();
add(u[i],v[i]);
}
n--; a[1][n+1]=-1.0;
for(int u=1;u<=n;u++){
a[u][u]=-1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=n+1) a[u][v]=1.0/d[v];
}
}
gauss();
for(int i=1;i<=m;i++)
g[i]=a[u[i]][n+1]/d[u[i]]+a[v[i]][n+1]/d[v[i]];
sort(g+1,g+m+1);
for(int i=1;i<=m;i++)
ans+=g[i]*(m-i+1);
printf("%.3lf\n",ans);
return 0;
}