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;
}
辣鸡老年选手AFO在即