[BZOJ 3143][Hnoi2013]游走(高斯消元+期望)
Description
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
Solution
对于点u(u≠1):到达u的概率 f[u]=∑f[v]/d[v] (Edges(u,v))
而f[1]=∑f[v]/d[v]+1 (Edges(1,v))
高斯消元可以求出所有点的概率
对于每条边 到达边i的概率 p[i]=f[u]/d[u]+f[v]/d[v]
贪心的编号然后求出期望就好了
好像BZOJ上的数据会比较水,洛谷数据…卡精度
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define eps 1e-7 using namespace std; int n,m,head[505],cnt=0,d[505]; double a[505][505],f[505],p[250005]; struct Node { int next,from,to; }Edges[500005]; void addedge(int u,int v) { Edges[++cnt].next=head[u]; head[u]=cnt; Edges[cnt].from=u; Edges[cnt].to=v; } void Gauss() { for(int i=1;i<=n;i++) { int maxline=i; for(int j=i+1;j<=n;j++) if(a[j][i]>a[maxline][i])maxline=j; if(maxline!=i) for(int j=i;j<=n+1;j++) swap(a[maxline][j],a[i][j]); for(int j=i+1;j<=n;j++) { if(fabs(a[j][i])<eps)continue; double t=a[j][i]/a[i][i]; for(int k=i;k<=n+1;k++) a[j][k]-=t*a[i][k]; } } for(int i=n;i>0;i--) { for(int j=n;j>i;j--) a[i][n+1]-=f[j]*a[i][j]; f[i]=a[i][n+1]/a[i][i]; } } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); d[u]++,d[v]++; } for(int i=1;i<n;i++) { a[i][i]=1; for(int j=head[i];~j;j=Edges[j].next) a[i][Edges[j].to]=-1.0/d[Edges[j].to]; } a[1][n+1]=1,a[n][n]=1; Gauss(); for(int i=1;i<=cnt;i+=2) { int u=Edges[i].from,v=Edges[i].to; p[(i+1)>>1]+=(double)f[u]/d[u]; p[(i+1)>>1]+=(double)f[v]/d[v]; } sort(p+1,p+1+m); double ans=0; for(int i=1;i<=m;i++) ans+=p[i]*(m-i+1); printf("%.3lf\n",ans); return 0; }
被卡精度)