BZOJ 3534 [Sdoi2014]重建

来源:互联网 发布:淘宝开店宝软件 编辑:程序博客网 时间:2024/05/09 11:08

矩阵-树定理 + 概率

挂题解呀:http://blog.csdn.net/iamzky/article/details/41317333

想了解这方面就去看论文:《生成树的计数及其应用》周冬(没有链接,不用点了)

这道题告诉我们:邻接矩阵中的的权可以不是1,而是其他权值,比如概率(感性认识,猜想算行列式的时候其实就是在把矩阵中树的边权乘起来,类似于重边就直接加在边权上从而使它乘起来翻倍?不会证明)

然后我就被精度卡了很久,后来发现别的都不管用,必须要在下文代码注释的地方那样打才能过。这样会把这一项绝对值最大的提上来。

原因:我猜是用大的放上面去消小的,大的那行乘的倍数是小/大,倍数较小,乘出来的值就较小,能够保证小的被消完之后,余下的f[j][i+1],f[j][i+2]…等项能保持较小。用小的去消大的,由于用的倍数是大/小,所以余下的项可能变得较大。然后就炸精度了(吗)。应该是个好习惯。

#include<cstdio>#include<cmath>#include<algorithm>#define N 52using namespace std;namespace runzhe2000{    const double eps = 1e-7;    int n;    double f[N][N], pro = 1;    double Gauss()    {        double det = 1;        for(int i = 1; i < n; i++)        {            /*↓↓↓把绝对值最大的一行换上来↓↓↓*/            int r = i;            for(int j = i; j < n; j++)                if(fabs(f[j][i]) > fabs(f[r][i])) r = j;            if(r != i) for(int j = 1; j < n; j++) swap(f[i][j], f[r][j]);            /*↑↑↑要不然我也不知道为什么会被卡精度↑↑↑*/              if(-eps < f[i][i] && f[i][i] < eps) return 0;                   for(int j = i+1; j < n; j++)            {                double tmp = -f[j][i]/f[i][i];                for(int k = i; k <= n; k++)                    f[j][k] += tmp * f[i][k];            }            det *= f[i][i];        }        return det < 0 ? -det : det;    }    void main()    {        scanf("%d",&n);        for(int i = 1; i <= n; i++)            for(int j = 1; j <= n; j++)            {                scanf("%lf",&f[i][j]);                if(f[i][j] > 1-eps) f[i][j] -= eps;                if(i < j)pro *= (1-f[i][j]);                f[i][j] /= (1-f[i][j]);            }        for(int i = 1; i <= n; i++)        {            double sum = 0;            for(int j = 1; j <= n; j++) sum += f[i][j];            f[i][i] = -sum;        }        printf("%.8lf\n",Gauss() * pro);    }}int main(){    runzhe2000::main();}
0 0
原创粉丝点击