[bzoj3143] [HNOI2013]游走
Description
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
Input
第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。
Output
仅包含一个实数,表示最小的期望值,保留3位小数。
Sample Input
3 3
2 3
1 2
1 3
Sample Output
3.333
Solution
问题可以转化为求每条边期望被经过多少次,然后期望大的编号小就行了。
对于每条边,它连接的两个点每次有一定概率走这条边,所以这条边的期望就是:
\[\frac{E(u)}{deg(u)}+\frac{E(v)}{deg(v)}
\]
所以问题转化为求一个点期望被经过多少次。
设\(E(x)\)表示这个点期望经过次数,可以列出式子:
\[E(x)=\sum_{(x,v)\in Edge} \frac{E(v)}{deg(v)}
\]
然后列方程解出来就行了。
对于最后一个点,走到它就结束了,所以忽略\(n\)号点连出来的边。
同样,算边的期望时也忽略。
对于第一个点,期望应为:
\[E(1)=\sum_{(1,v)\in Edge}\frac{E(v)}{deg(v)}+1
\]
因为一开始就经过了一次。
#include<bits/stdc++.h>
using namespace std;
#define ONLINE_JUDGE
#ifdef ONLINE_JUDGE
#define getchar() ((p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2)?EOF:*p1++)
#endif
namespace fast_IO {
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T> inline void read(T &x) {
x=0;T f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
template <typename T,typename... Args> inline void read(T& x,Args& ...args) {
read(x),read(args...);
}
char buf2[1<<21],a[80];int p,p3=-1;
inline void flush() {fwrite(buf2,1,p3+1,stdout),p3=-1;}
template <typename T> inline void write(T x) {
if(p3>(1<<20)) flush();
if(x<0) buf2[++p3]='-',x=-x;
do {a[++p]=x%10+48;} while(x/=10);
do {buf2[++p3]=a[p];} while(--p);
buf2[++p3]='\n';
}
template <typename T,typename... Args> inline void write(T x,Args ...args) {
write(x),write(args...);
}
}
using fast_IO :: read;
using fast_IO :: write;
using fast_IO :: flush;
#define lf double
const lf eps = 1e-5;
const int maxn = 5e2+10;
lf f[maxn][maxn],w[maxn*maxn];
int n,m,head[maxn],tot,deg[maxn];
struct edge{int to,nxt;}e[maxn*maxn];
void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
void ins(int u,int v) {add(u,v),add(v,u);}
void gauss() {
for(int i=1;i<=n;i++) {
if(fabs(f[i][i])<eps)
for(int j=i;j<=n;j++)
if(fabs(f[j][i])>eps) {
for(int k=1;k<=n+1;k++)
swap(f[i][k],f[j][k]);
break;
}
for(int j=i+1;j<=n;j++) {
lf t=f[j][i]/f[i][i];
for(int k=i;k<=n+1;k++) f[j][k]=f[j][k]-f[i][k]*t;
}
}
for(int i=n;i;i--) {
lf tmp=f[i][n+1];
for(int j=i+1;j<=n;j++) tmp-=f[j][j]*f[i][j];
tmp/=f[i][i];f[i][i]=tmp;
}
}
int main() {
read(n,m);
for(int i=1,x,y;i<=m;i++) read(x,y),ins(x,y),deg[x]++,deg[y]++;
for(int x=1;x<=n;x++) {
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=n) f[x][e[i].to]-=1.0/deg[e[i].to];
f[x][x]+=1.0;
}f[1][n+1]+=1.0;
gauss();f[n][n]=0;
for(int i=1;i<=tot;i++)
w[(i+(i&1))>>1]+=f[e[i].to][e[i].to]/(lf)deg[e[i].to];
sort(w+1,w+tot/2+1);lf ans=0;
for(int i=1;i<=tot/2;i++) ans+=w[i]*(lf)(tot/2-i+1);
printf("%.3lf\n",ans);
flush();
return 0;
}