【BZOJ3534】[SDOI2014] 重建(矩阵树定理)

点此看题面

大致题意: 给你一张图,每条边有一定存在概率。求存在的图刚好为一棵树的概率。

矩阵树定理是什么

如果您不会矩阵树定理,可以看看蒟蒻的这篇博客:初学矩阵树定理

矩阵树定理的应用

此题中,直接根据\(p_{i,j}\)来套矩阵树定理显然是不可以的。

考虑我们把每个\(p_{i,j}\)变成\(\frac{p_{i,j}}{1-p_{i,j}}\),套用矩阵树定理,然后最后将结果乘上\(\prod_{i=1}^n\prod_{j=i+1}^n(1-p_{i,j})\),就是答案了。

此时度数矩阵和邻接矩阵中的值都应该用\(\frac p{1-p}\)去替换原先的\(1\)

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50
#define DB double
#define eps 1e-8
using namespace std;
int n;double a[N+5][N+5];
class MatrixTreeSolver
{
	private:
		class Mat//矩阵
		{
			private:
				int n;DB v[N+5][N+5];
				I bool FindLine(CI x)
				{
					for(RI i=x+1;i<=n;++i)
					{
						if(fabs(v[i][x])<eps) continue;
						for(RI j=x;j<=n;++j) swap(v[x][j],v[i][j]);return true;
					}return false;
				}
			public:
				I Mat(CI x=0):n(x){memset(v,0,sizeof(v));}
				I DB *operator [] (CI x) {return v[x];}
				I DB Det()//行列式
				{
					RI i,j,k,op=1;DB t,res=1;for(i=1;i<=n;++i)
					{
						if(fabs(v[i][i])<eps&&(op*=-1,!FindLine(i))) return 0;res*=v[i][i];
						for(j=i+1;j<=n;++j) for(t=v[j][i]/v[i][i],k=i;k<=n;++k) v[j][k]-=t*v[i][k];
					}return op*res;
				}
		}S;
	public:
		I void Solve()
		{
			RI i,j;DB t,res=1;S=Mat(n-1);
			for(i=1;i<=n;++i) for(j=i+1;j<=n;++j)
				(t=1-a[i][j])<eps&&(t=eps),a[i][j]/=t,res*=t,//求出矩阵中这一位的值
				S[i][i]+=a[i][j],S[j][j]+=a[i][j],S[i][j]-=a[i][j],S[j][i]-=a[i][j];//求出度数矩阵减邻接矩阵
			printf("%.8lf",S.Det()*res);//求答案
		}
}T;
int main()
{
	RI i,j;for(scanf("%d",&n),i=1;i<=n;++i) for(j=1;j<=n;++j) scanf("%lf",&a[i][j]);//读入
	return T.Solve(),0;
}
posted @ 2019-08-07 20:36  TheLostWeak  阅读(135)  评论(0编辑  收藏