题目大意:
给你
个点的完全图和一棵
个点的树
,求有多少棵生成树,满足有
条边在树
上。
对于任意
都要回答询问。
分析:
生成树计数可以想到矩阵树定理,但是显然矩阵树跑出来的值是边权的乘积的和,不能统计该树上的边与给出的树的重合边数。
考虑把一条边看做一个多项式,如果这条边在原树上,这条边的权值看作是
,否则看做
。于是一棵树的总权值就是
,其中
是与原树重合的边的数目。然后我们可以把这些多项式求和,用矩阵树算出来,总和就是一个多项式,其中
的系数表示有
条边与原树相同。
我们不能把多项式带人进去算,但是我们知道最终的多项式的次数是
的。所以我们可以取
个数代替
去求行列式,因为是要求出系数,所以直接暴力高斯消元插值就好了。复杂度是
的。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define LL long long
const int maxn=105;
const LL mod=1e9+7;
using namespace std;
int n,x,y;
LL a[maxn][maxn],c[maxn][maxn],b[maxn];
LL power(LL x,LL y)
{
if (y==1) return x;
LL c=power(x,y/2);
c=(c*c)%mod;
if (y%2) c=(c*x)%mod;
return c;
}
LL det(int n)
{
LL ans=1;
for (int i=1;i<=n;i++)
{
LL inv=power(c[i][i],mod-2);
for (int j=i+1;j<=n;j++)
{
LL rate=c[j][i]*inv%mod;
for (int k=i;k<=n;k++)
{
c[j][k]=(c[j][k]+mod-rate*c[i][k]%mod)%mod;
}
}
ans=(ans*c[i][i])%mod;
if (!c[i][i]) break;
}
return ans;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
a[x][y]=1;
a[y][x]=1;
}
for (int k=1;k<=n;k++)
{
memset(c,0,sizeof(c));
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
if (i!=j)
{
if (a[i][j]) c[i][j]=mod-k,c[i][i]+=k;
else c[i][j]=mod-1,c[i][i]+=1;
}
}
}
b[k]=det(n-1);
}
for (int i=1;i<=n;i++)
{
c[i][1]=1;
for (int j=2;j<=n;j++) c[i][j]=(c[i][j-1]*i)%mod;
}
for (int i=1;i<=n;i++)
{
LL inv=power(c[i][i],mod-2);
for (int j=1;j<=n;j++)
{
if (i==j) continue;
LL rate=c[j][i]*inv%mod;
for (int k=i;k<=n;k++)
{
c[j][k]=(c[j][k]+mod-rate*c[i][k]%mod)%mod;
}
b[j]=(b[j]+mod-rate*b[i]%mod)%mod;
}
}
for (int i=1;i<=n;i++)
{
b[i]=b[i]*power(c[i][i],mod-2)%mod;
printf("%lld ",b[i]);
}
}