P3232 [HNOI2013]游走

传送门

一看题面就是高斯消元解 $dp$ 方程组,设 $f[x]$ 表示从起点到终点,经过节点 $x$ 的期望次数

那么对于一个点 $x$,枚举所有相连的边 $(x,v)$ ,其中 $v \neq n$,设节点 $v$ 的出度为 $du[v]$ ,那么有

$f[x]=\sum_{v}\frac {f[v]} {du[v]}$

然后对于 $n$ 个点都有一个方程,直接解方程组即可

然后发现题目有点意思,问的是给边安排权值使得得到的权值期望最小,冷静分析一波,刚刚高斯消元解出来的东西肯定有用

如果我们能确定每条边被经过的期望次数,那么直接按期望从小到大给从大到小的权值即可

发现对于一条边,它被经过的期望次数其实和它连接的两点有关,具体地:

设边为 $(u,v)$ ,那么它被经过的期望次数就是 $\frac {f[u]} {du[u]}+\frac {f[v]} {du[v]}$

所以这题就解决了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef double db;
inline 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<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=507,M=N*N;
int n,m,du[N];
int fir[N],from[M],to[M],cntt;
inline void add(int a,int b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; }
db A[N][N],ans[N],p[M];
void Gauss()
{
    for(int i=1;i<=n;i++)
    {
        int pos=i;
        for(int j=i+1;j<=n;j++)
            if(fabs(A[j][i])>fabs(A[pos][i])) pos=j;
        swap(A[i],A[pos]);
        for(int j=i+1;j<=n;j++)
        {
            db 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>=1;i--)
    {
        for(int j=i+1;j<=n;j++) A[i][n+1]-=A[i][j]*ans[j];
        ans[i]=A[i][n+1]/A[i][i];
    }
}
int d[M][2];
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        d[i][0]=read(),d[i][1]=read();
        add(d[i][0],d[i][1]),add(d[i][1],d[i][0]);
        du[d[i][0]]++,du[d[i][1]]++;
    }
    for(int i=1;i<=n;i++) A[i][i]=1;
    for(int i=1;i<n;i++)
        for(int j=fir[i];j;j=from[j])
        {
            int &v=to[j];
            A[v][i]-=1.0/du[i];
        }
    A[1][n+1]=1; Gauss(); db sum=0;
    for(int i=1;i<=m;i++)
    {
        if(d[i][0]!=n) p[i]+=ans[d[i][0]]/du[d[i][0]];
        if(d[i][1]!=n) p[i]+=ans[d[i][1]]/du[d[i][1]];
    }
    sort(p+1,p+m+1); reverse(p+1,p+m+1);
    for(int i=1;i<=m;i++) sum+=p[i]*i;
    printf("%.3lf\n",sum);
    return 0;
}

 

posted @ 2019-09-11 13:34  LLTYYC  阅读(153)  评论(0编辑  收藏  举报