【CF917D】Stranger Trees

题目

题目链接:https://codeforces.com/problemset/problem/917/D
给出 \(n\) 个点的一棵树,求在 \(n\) 个点的完全图的所有生成树中,与给出的树边数有 \(k\) 条重合的生成树数量。对于每一个 \(k\in [0,n)∩\mathrm{Z}\) 输出结果模 \(10^9+7\) 后的值。
\(n\leq 100\)

思路

把给出的树中的边的边权设为 \(x\),其他边边权设为 \(1\),那么对于完全图中任意一棵生成树 \(\mathrm{T}\),设其边权积为 \(s\),它与给定的树重合的边的数量即为 \(\log_x s\)
那么我们枚举 \(x=1\sim n\),分别矩阵树定理,设 \(f_i\) 表示 \(x=i\) 时所有生成树权值之和。设当 \(x=i\) 时,与给定的树重合边数为 \(j\) 的生成树数量为 \(g_{i,j}\),那么我们可以得到

\[\left\{\begin{matrix} 1^0g_{1,0}+1^1g_{1,1}+\cdots+1^{n-1}g_{1,n}=f_1 \\ 2^0g_{2,0}+2^1g_{2,1}+\cdots+2^{n-1}g_{2,n}=f_2 \\ \vdots \\ n^0g_{n,0}+n^1g_{n,1}+\cdots+n^{n-1}g_{n,n}=f_n \end{matrix}\right.\]

高斯消元即可。
时间复杂度 \(O(n^4)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=105,MOD=1000000007;
int n,G[N][N];
ll f[N],g[N][N];

ll fpow(ll x,ll k)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%MOD)
		if (k&1) ans=ans*x%MOD;
	return ans;
}

ll dit()
{
	ll tag=1,res=1;
	for (int i=1;i<n;i++)
	{
		for (int j=i;j<n;j++)
			if (g[j][i])
			{
				if (i!=j) tag=-tag;
				for (int k=1;k<n;k++)
					swap(g[j][k],g[i][k]);
				break;
			}
		ll inv=fpow(g[i][i],MOD-2);
		for (int j=i+1;j<n;j++)
			if (g[j][i])
			{
				ll base=inv*g[j][i]%MOD;
				for (int k=1;k<n;k++)
					g[j][k]=((g[j][k]-base*g[i][k])%MOD+MOD)%MOD;
			}
	}
	for (int i=1;i<n;i++) res=res*g[i][i]%MOD;
	return (res*tag+MOD)%MOD;
}

void gauss()
{
	for (int i=1;i<=n;i++)
	{
		for (int j=i;j<=n;j++)
			if (g[j][i])
			{
				for (int k=1;k<=n;k++)
					swap(g[i][k],g[j][k]);
				swap(f[i],f[j]);
				break;
			}
		ll inv=fpow(g[i][i],MOD-2);
		for (int j=i+1;j<=n;j++)
			if (g[j][i])
			{
				ll base=g[j][i]*inv%MOD;
				for (int k=1;k<=n;k++)
					g[j][k]=((g[j][k]-g[i][k]*base)%MOD+MOD)%MOD;
				f[j]=((f[j]-f[i]*base)%MOD+MOD)%MOD;
			}
	}
	for (int i=n;i>=1;i--)
	{
		ll sum=0;
		for (int j=i+1;j<=n;j++)
			sum=(sum+g[i][j]*f[j])%MOD;
		f[i]=(f[i]-sum+MOD)*fpow(g[i][i],MOD-2)%MOD;
	}
}

int main()
{
	scanf("%d",&n);
	for (int i=1,x,y;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		G[x][y]=G[y][x]=1;
	}
	for (int i=1;i<=n;i++)
	{
		memset(g,0,sizeof(g));
		for (int j=1;j<=n;j++)
			for (int k=1;k<=n;k++)
				if (j!=k && G[j][k])
					g[j][k]=MOD-i,g[j][j]=(g[j][j]+i)%MOD;
				else if (j!=k)
					g[j][k]=MOD-1,g[j][j]=(g[j][j]+1)%MOD;
		f[i]=dit();
	}
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			g[i][j]=fpow(i,j-1);
	gauss();
	for (int i=1;i<=n;i++)
		printf("%d ",f[i]);
	return 0;
}
posted @ 2021-01-19 09:50  stoorz  阅读(62)  评论(0编辑  收藏  举报