传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3143
思路:首先贪心是很显然的,为了使分数最小,期望到达次数越多的,编号就应该给的越小。
直接设边的期望列方程比较复杂。
所以我们换一个思路,先解出每个点的期望到达次数,那么边的期望次数就可以算出来了。
设边为y,与它相连的两个点为a,b,点的期望次数为f[a],f[b],度数为deg[a],deg[b];
那么边的期望次数g[y]就是f[a]/deg[a]+f[b]/deg[b]。
这很好理解,因为一条边只会从这两个点走过来。
现在考虑求点
对于一个点x,它只会从相邻的点过来,那么方程就是
f[x]=∑f[son[x]]/deg[x];
除了n号点,我们就可以得到有n-1个未知数的n-1个方程了
高斯消元即可
#include<cmath> #include<cstdio> #include<cstring> #include<algorithm> const int maxn=510,maxm=300010; const double eps=1e-10; using namespace std; typedef double ld; int pre[maxm],now[maxn],son[maxm],tot,deg[maxn],n,m,st[maxm],ed[maxm];ld a[maxn][maxn],f[maxn],g[maxm],ans; void add(int x,int y){pre[++tot]=now[x],now[x]=tot,son[tot]=y,deg[x]++;} void gauss(){ for (int i=1;i<=n;i++){ int id=i;ld maxs=-1.0; for (int j=i;j<=n;j++) if (fabs(a[j][i])+eps>maxs) id=j,maxs=fabs(a[j][i]); if (id!=i) for (int j=1;j<=n+1;j++) swap(a[i][j],a[id][j]); ld t=a[i][i];if (fabs(t)<eps) for (;;); for (int j=1;j<=n+1;j++) a[i][j]/=t; for (int j=1;j<=n;j++) if (j!=i){ ld t=a[j][i]; for (int k=1;k<=n+1;k++) a[j][k]-=t*a[i][k]; } } for (int i=1;i<=n;i++) f[i]=a[i][n+1]; } int main(){ scanf("%d%d",&n,&m); for (int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x),st[i]=x,ed[i]=y; n--,a[1][n+1]=-1.0; for (int x=1;x<=n;x++){ a[x][x]=-1.0; for (int y=now[x];y;y=pre[y]) if (son[y]!=n+1) a[x][son[y]]=1.0/deg[son[y]]; } gauss(); for (int i=1;i<=m;i++) g[i]=f[st[i]]/deg[st[i]]+f[ed[i]]/deg[ed[i]]; sort(g+1,g+1+m); for (int i=1;i<=m;i++) ans+=g[i]*(m-i+1); printf("%.3lf\n",(double)ans); return 0; }