CF917D Stranger Trees

好久没写博客了,趁着专业课考完去写下今年数学专题里感觉比较好的题

首先图的生成树计数肯定离不开矩阵树定理,这里由于是无向带权图,需要构造扩展的基尔霍夫矩阵

即度数矩阵为每个点连出的边权和;邻接矩阵为两点间的边权

这样求出的矩阵的行列式的值是该图所有生成树中边权乘积的和,即:

\[\sum_T \prod_{(u,v)\in T} val(u,v) \]

考虑如何统计生成树与给定树边的重复条数,很容易想到生成函数,将重合的边权值标为\(x\),不重合的边权值标为\(1\)

此时上面求出的式子就是一个关于\(x\)的多项式了,不难发现\(x^i\)的系数就是恰好有\(i\)条边重合时的方案数

现在的问题是我们要求出这个多项式的系数,这个也很好办,由于数据范围很小,我们可以暴力设\(x=1,2\dots,n\),列出关于系数的方程组

然后大力高斯消元求解所有的系数即可,总复杂度\(O(n^4)\)

#include<cstdio>
#include<iostream>
#include<vector>
#include<assert.h>
#define RI register int
#define CI const int&
using namespace std;
const int N=105,mod=1e9+7;
int n,x,y,T[N][N],DT[N];
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
inline void dec(int& x,CI y)
{
	if ((x-=y)<0) x+=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline int Gauss_Det(vector <vector <int>>& vec)
{
	RI i,j,k; int n=vec.size(),det=1;
	for (i=0;i<n-1;++i)
	{
		for (j=i;!vec[j][i];++j);
		if (j>=n-1) return 0;
		if (i!=j) swap(vec[i],vec[j]),det=(mod-det)%mod;
		for (j=i+1;j<n-1;++j)
		{
			int tmp=1LL*vec[j][i]*quick_pow(vec[i][i])%mod;
			for (k=i;k<n-1;++k) dec(vec[j][k],1LL*vec[i][k]*tmp%mod);
		}
		det=1LL*det*vec[i][i]%mod;
	}
	return det;
}
inline bool Gauss(vector <vector <int>>& vec)
{
	RI i,j,k; int n=vec.size();
	for (i=0;i<n;++i)
	{
		for (j=i;!vec[j][i];++j);
		if (j>=n) return 0;
		swap(vec[i],vec[j]);
		for (j=i+1;j<n;++j)
		{
			int tmp=1LL*vec[j][i]*quick_pow(vec[i][i])%mod;
			for (k=i;k<=n;++k) dec(vec[j][k],1LL*vec[i][k]*tmp%mod);
		}
	}
	for (i=n-1;i>=0;--i)
	{
		for (j=i+1;j<n;++j) dec(vec[i][n],1LL*vec[j][n]*vec[i][j]%mod);
		vec[i][n]=1LL*vec[i][n]*quick_pow(vec[i][i])%mod;
	}
	return 1;
}
int main()
{
	RI i,j,k; for (scanf("%d",&n),i=1;i<n;++i)
	scanf("%d%d",&x,&y),T[x][y]=T[y][x]=1,++DT[x],++DT[y];
	vector<vector<int>> A(n,vector<int>(n+1,0));
	for (k=1;k<=n;++k)
	{
		vector<vector<int>> K(n,vector<int>(n,0));
		for (i=1;i<=n;++i) for (j=1;j<=n;++j)
		if (i==j) K[i-1][j-1]=(1LL*DT[i]*k%mod+(n-1-DT[i]))%mod;
		else K[i-1][j-1]=T[i][j]?mod-k:mod-1;
		A[k-1][n]=Gauss_Det(K); int cur=1;
		for (i=0;i<n;++i,cur=1LL*cur*k%mod)	A[k-1][i]=cur;
	}
	bool valid=Gauss(A); assert(valid);
	for (i=0;i<n;++i) printf("%d%c",A[i][n]," \n"[i==n-1]);
	return 0;
}
posted @ 2024-06-20 16:57  空気力学の詩  阅读(12)  评论(0编辑  收藏  举报