【题解】dutoj1081 Prufer 序列

题目链接

题意:略(原题的描述已经很简练了)。

分析:首先需要知道的是一个prufer序列和一棵标号树是一一对应的,所以不会有S=0的情况。一个标号在prufer序列中没出现,当且仅当该标号对应的点在prufer序列对应的树中为叶结点。对图G,设其不同的生成树有cnt棵,令ans=cnt*n*(n+1)/2。考虑点u对ans的负贡献,即计算以u为叶结点的生成树的个数,设为cntu,则ans=ans-cntu*u。计算生成树个数用矩阵树定理,计算以u为叶结点的生成树个数可以先计算图G-u的生成树个数cnt0,则cntu=cnt0*du。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=100+5,mo=1e9+7;
void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
	if (!b) {d=a;x=1;y=0;}
	else {gcd(b,a%b,d,y,x);y-=x*(a/b);}
}
LL inv(LL a,LL n)
{
	LL d,x,y;
	gcd(a,n,d,x,y);
	return d==1?(x+n)%n:-1;
}
struct mat
{
	int n;
	LL e[maxn][maxn];
	void clear()
	{
		n=0;memset(e,0,sizeof(e));
	}
	LL det()
	{
		
		
		/*for (int i=1;i<=n;i++)
		{
			for (int j=1;j<=n;j++) cout<<e[i][j]<<" ";
		    cout<<endl;
		}
		cout<<endl;*/
		
		
		for (int i=1;i<=n;i++)
		{
			int r=0;
			for (int k=i;k<=n;k++) if (e[k][i]) {r=k;break;}
			if (!r) return 0;
			if (r!=i) for (int j=1;j<=n;j++) swap(e[r][j],e[i][j]);
			LL iv=inv(e[i][i],mo);
			for (int k=i+1;k<=n;k++)
			    if (e[k][i])
			    {
			        LL o=e[k][i]*iv%mo;
					for (int j=1;j<=n;j++) e[k][j]=(e[k][j]-o*e[i][j]%mo+mo)%mo;
			    }
		}
		LL ret=1;
		for (int i=1;i<=n;i++) ret=ret*e[i][i]%mo;
		
		
		//cout<<ret<<endl;
		
		return ret;
	}
};
int n,m,a[maxn][maxn],d[maxn];
mat A;
void init()
{
	memset(a,0,sizeof(a));
	memset(d,0,sizeof(d));
}
int main()
{
	int T;cin>>T;
	while (T--)
	{
		scanf("%d%d",&n,&m);
		init();
		while (m--)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			a[u][v]=a[v][u]=1;
			d[u]++;d[v]++;
		}
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++)
			    A.e[i][j]=(mo-a[i][j])%mo;
		for (int i=1;i<=n;i++) A.e[i][i]=d[i];
		A.n=n-1;
		LL ans=A.det()*(LL)(n*(n+1)/2)%mo;
		for (int u=1;u<=n;u++)
		{
			A.clear();
			for (int i=1;i<u;i++)
			{
				for (int j=1;j<u;j++) A.e[i][j]=(mo-a[i][j])%mo;
				for (int j=u+1;j<=n;j++) A.e[i][j-1]=(mo-a[i][j])%mo;
			}
			for (int i=1;i<u;i++) A.e[i][i]=d[i]-a[i][u];
			for (int i=u+1;i<=n;i++)
			{
				for (int j=1;j<u;j++) A.e[i-1][j]=(mo-a[i][j])%mo;
				for (int j=u+1;j<=n;j++) A.e[i-1][j-1]=(mo-a[i][j])%mo;
			}
			for (int i=u+1;i<=n;i++) A.e[i-1][i-1]=d[i]-a[i][u];
			A.n=n-2;
			ans=(ans-A.det()*d[u]*u%mo+mo)%mo;
		}
		printf("%d\n",(ans+mo)%mo);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值