bzoj 3143
高斯消元解期望方程组题目
其实所谓解期望方程组,就是解普通方程组,只是这个方程组是根据期望的定义列出的。
对于本题,每一条边对总期望的贡献就是这条边的边权乘上这条边期望的经过次数,所以我们求出每条边期望的经过次数后从大到小排序,然后从小到大赋边权(编号)即可
所以问题就转化成了如何求出每条边经过次数的期望
由于边的数量很多难以计算,所以我们转化一下,去计算这条边两端点的经过次数的期望
设某一点经过次数的期望是$E_i$,某一点的出边条数是$d_i$,设一条边的两端分别为$u,v$,那么一条边经过次数的期望就是$\frac{E_u}{d_u}+\frac{E_v}{d_v}$!(这个公式有一个细节问题,我们最后来谈)
原因很简单,边是双向边,一条边的期望是两端点各自被经过的期望乘这个点走上这条边的概率!
所以我们只需求出所有点的经过次数的期望即可
而我们知道,一个点能被到达的情况就是他周围点各自被到达的期望乘对应点走向这个点的概率
设边集为E,那么对于任意一个点,有:
$E_i=\sum_{(i,v)\in E}\frac{E_v}{d_v}$(这个公式有另一个细节问题,我们同样最后再谈)
那么我们整理一下,就是:
$E_i-\sum_{(i,v)\in E}\frac{E_v}{d_v}=0$
其中所有的$E_i$为未知数,那么可以利用高斯消元解之
可是你是否发现了一个问题?
是的!这样解出来最后所有变量都是0啊!
为什么?
因为我们忽略了两个问题,也就是我们上面提到的两个细节:
首先,由于我们是从1号点开始的,所以1号点的期望本身自带1!
因此,对于1号点的公式,应该长这样:$E_1=\sum_{(1,v)\in E}\frac{E_v}{d_v}+1$
这样是不就好多了?
然后,我们应当注意到:终点是不会再向外出的!
因此终点不应该对周围的边(或者点)产生任何贡献!
设终点为$t$
对于边的期望公式我们修正成这样:
$E_{(u,v)}=(u\neq t)\frac{E_u}{d_u}+(v\neq t)\frac{E_v}{d_v}$
对于点的期望公式我们修正成这样:
$E_i=(i==1)+\sum_{(i,v)\in E \wedge v\neq t}\frac{E_v}{d_v}$
这就可以解了
最后统计出所有边的期望,然后排序计算输出答案即可
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <vector> using namespace std; const double eps=0.000001; double inr[505]; vector <int> v[505]; int n,m; struct E { int l,r; double v; friend bool operator < (E x,E y) { return x.v>y.v; } }e[250005]; double a[505][505]; void get_l(int i) { scanf("%d%d",&e[i].l,&e[i].r); inr[e[i].l]+=1.0,inr[e[i].r]+=1.0; v[e[i].l].push_back(e[i].r),v[e[i].r].push_back(e[i].l); } void Gauss() { for(int i=1;i<n;i++) { int temp=i; while(fabs(a[temp][i])<=eps)temp++; if(temp!=i)for(int j=i;j<=n;j++)swap(a[temp][j],a[i][j]); double now=a[i][i]; for(int j=i;j<=n;j++)a[i][j]/=now; for(int j=i+1;j<n;j++) { if(!a[j][i])continue; now=a[j][i]; for(int k=i;k<=n;k++)a[j][k]-=now*a[i][k]; } } for(int i=n-1;i;i--) { for(int j=i-1;j;j--)a[j][n]-=a[i][n]*a[j][i]; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++)get_l(i); for(int i=1;i<n;i++) { if(i==1)a[i][n]=1; a[i][i]=1; for(int j=0;j<v[i].size();j++)if(v[i][j]!=n)a[i][v[i][j]]=-1.0/inr[v[i][j]]; } Gauss(); for(int i=1;i<=m;i++)e[i].v=a[e[i].l][n]/inr[e[i].l]+a[e[i].r][n]/inr[e[i].r]; sort(e+1,e+m+1); double ans=0; for(int i=1;i<=m;i++)ans+=e[i].v*(double)i; printf("%.3lf\n",ans); return 0; }