洛谷3317 SDOI2014重建(高斯消元+期望)

qwq
一开始想了个错的做法。

直接开始说比较正确的做法吧。
首先我们考虑题目的\(ans\)该怎么去求
我们令\(x\)表示原图中的某一条边

\[ans = \sum \prod_{x\in tree} p_x \prod_{x\ not\in tree} (1-p_x)\]

qwq而根据矩阵树定理,我们可以求出来所有生成树的边权乘积的和,也就是前一部分。

现在我们考虑应该怎么优化第二部分。
qwq
我们经过推理能发现,我们可以用总的除去在生成树里面的求出来不在生成树里面的。

也就是说
\[\prod_{x\ not \in tree} (1-p_x)= \frac{\prod (1-p_i)}{\prod_{x\in tree} (1-p_j)}\]

我们带回原柿,然后把\(\prod (1-p_i)\)提出来

\[ans = \prod (1-p_x) \times \sum \prod_{x \in tree} \frac{p_x}{1-p_x}\]

那么现在,对于后面那一项,我们只需要把所有的边都设成权值是\(\prod_{x \in tree} \frac{p_x}{1-p_x}\)
然后每个\(d[i]\)表示与他连接的所有边权的和。

直接跑矩阵树定理就能求出来\(sum\)啦,然后直接用一开始求的\(\prod p_x\),一减就OK了

但是这里有一个需要注意的地方就是当\(p_x\)等于\(1\)的时候,我们应该将他的权值设成\(1-eps\)

因为当\(p\)等于1的时候,\(\frac{1}{1-p} -> inf\)

然后有因为\(\frac{1}{eps}->inf\)

所以\(p=1-eps\)

然后弄完权值直接跑矩阵树定理就好

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#include<ctime>
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 110;
const double eps = 1e-6;
double a[maxn][maxn];
double d[maxn];
int n;
double ans=1;
void gauss()
{
    int k=1;
    for (int i=1;i<=n;i++)
    {
        int now = k;
        while(now<=n && fabs(a[now][i])<=eps)  now++;
        if (now==n+1) continue;
        for (int j=1;j<=n+1;j++) swap(a[now][j],a[k][j]);
        for (int j=1;j<=n;j++)
        {
            if (j!=k)
            {
                double t = a[j][i]/a[k][i];
                for (int p=1;p<=n+1;p++) a[j][p]-=t*a[k][p];
            }
        }
        ++k;
    }
    for (int i=1;i<=n;i++)
      ans=ans*a[i][i];
}
double ymh=1;
int main()
{
  n=read();
  for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
    {
        double x;
        scanf("%lf",&x);
        if (x==1) x = 1-eps;
        if (i<j) ymh=ymh*(1-x);
        x=x/(1-x);
        a[i][j]=-x;
        d[i]+=x;
        //d[j]+=x;
    }
  for (int i=1;i<=n;i++) a[i][i]=d[i];
  gauss();
  printf("%.5lf",ans*ymh);
  return 0;
}

转载于:https://www.cnblogs.com/yimmortal/p/10152025.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值