[BZOJ]3143: [Hnoi2013]游走 期望+高斯消元

来源:互联网 发布:网易邮件客户端 mac 编辑:程序博客网 时间:2024/05/17 00:50

Description

一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

题解:

易得只要得知每条边的期望走过次数,就可以得到答案,但是这个并不好求。转化一下,求每个点的期望走过次数,那么每条边的也可以被算出来了。算点的期望简单很多,注意一下两个特殊点:1号点,算的时候要+1;n号点,期望走过次数为1,然后高斯消元一下就好了,跟BZOJ2337的套路差不多。另外,这道题卡精度!eps开1e-8会WA!1e-10才AC!

代码:

#include<bits/stdc++.h>using namespace std;#define LL long longconst int Maxn=510;const long double eps=1e-10;int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}    return x*f;}int n,m;struct Edge{int x,y,next;}e[Maxn*Maxn*2];int last[Maxn],len=0;void ins(int x,int y){    int t=++len;    e[t].x=x;e[t].y=y;e[t].next=last[x];last[x]=t;}long double a[Maxn][Maxn],dis[Maxn*Maxn];int ex[Maxn*Maxn],ey[Maxn*Maxn];void gauss(){    for(int i=1;i<n;i++)    {        if(abs(a[i][i])<=eps)        {            for(int j=i+1;j<n;j++)            if(abs(a[j][i])>eps)            {                for(int k=i;k<=n+1;k++)swap(a[j][k],a[i][k]);                break;            }        }        for(int j=i+1;j<n;j++)        if(abs(a[j][i])>eps)        {            long 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-1;i;i--)    {        for(int j=i+1;j<=n;j++)        a[i][n+1]-=a[i][j]*a[j][n+1];        a[i][n+1]/=a[i][i];    }}int degree[Maxn];int main(){    memset(a,0,sizeof(a));    n=read();m=read();    for(int i=1;i<=m;i++)    {        int x=read(),y=read();ex[i]=x;ey[i]=y;        ins(x,y);ins(y,x);degree[x]++;degree[y]++;    }    a[1][n+1]=-1.0;a[n][n]=a[n][n+1]=1.0;    for(int x=1;x<n;x++)    {        a[x][x]=-1.0;        for(int i=last[x];i;i=e[i].next)        {            int y=e[i].y;            if(y!=n)a[y][x]+=(1.0/(long double)(degree[x]));        }    }    gauss();    for(int i=1;i<=m;i++)    {        dis[i]=0.0;        if(ex[i]!=n)dis[i]+=a[ex[i]][n+1]/(long double)(degree[ex[i]]);        if(ey[i]!=n)dis[i]+=a[ey[i]][n+1]/(long double)(degree[ey[i]]);    }    sort(dis+1,dis+1+m);    long double ans=0.0;    for(int i=m;i;i--)    ans+=dis[m-i+1]*(long double)(i);    printf("%.3Lf",ans);}