bzoj3143: [Hnoi2013]游走

求经过边的期望次数,然后边的编号相当于给期望一个系数,期望大到小给编号就好

假如可以强行改边为点高斯消元的话是很方便的,然而并不资瓷

但是我们可以先把经过点的期望次数求出来:f(u)=sigema((u,v)属于E且v!=n)v f(v)/du(v),特别的,f(1)在右边还要加上一个1

对于1条边而言,走过它的期望次数就是它两端的点相互给对方的贡献

所以g(k)=f(u)/du(u)(当u!=n时)+f(u)/du(u)(当v!=n时)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

struct node
{
    int x,y,next;
}a[510000];int len,last[510];int du[510];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}

int n;
double qg[510][510],qc[510];
void gauss()
{
    for(int j=1;j<=n;j++)
    {
        for(int i=j;i<=n;i++)
            if(fabs(qg[i][j])>1e-8)
            {
                for(int k=1;k<=n;k++)swap(qg[i][k],qg[j][k]);
                swap(qc[i],qc[j]);
                break;
            }
        for(int i=1;i<=n;i++)
        {
            if(i==j)continue;
            double rate=qg[i][j]/qg[j][j];
            for(int k=1;k<=n;k++)
            qg[i][k]-=qg[j][k]*rate;
            qc[i]-=qc[j]*rate;
        }
    }
}

int glen;
double f[510],g[310000];
int main()
{
    int m,x,y;
    scanf("%d%d",&n,&m);
    len=0;memset(last,0,sizeof(last));
    memset(du,0,sizeof(du));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        ins(x,y),ins(y,x);
        du[x]++,du[y]++;
    }
    
    memset(qc,0,sizeof(qc));qc[1]=-1;
    for(x=1;x<=n;x++)
    {
        qg[x][x]=-1;
        for(int k=last[x];k;k=a[k].next)
        {
            y=a[k].y;
            if(y!=n)qg[x][y]=1.0/(double)du[y];
        }
    }
    gauss();
    for(int i=1;i<=n;i++)f[i]=qc[i]/qg[i][i];
    
    glen=0;
    for(int k=1;k<=len;k+=2)
    {
        x=a[k].x,y=a[k].y;
        g[++glen]=0;
        if(x!=n)g[glen]+=f[x]/(double)du[x];
        if(y!=n)g[glen]+=f[y]/(double)du[y];
    }
    sort(g+1,g+glen+1);
    double ans=0;
    for(int i=1;i<=glen;i++)ans+=g[i]*(double)(glen-i+1);
    printf("%.3lf\n",ans);
    
    return 0;
}

 

猜你喜欢

转载自www.cnblogs.com/AKCqhzdy/p/9882176.html