省选模拟赛20191211 T2 B【矩阵树定理+拉格朗日插值】

题目描述:

在这里插入图片描述

题目分析:

首先一定可以找到一种加边之后删边的方法对应一次性添加不超过k条非树边,然后断掉原来的树边形成一棵新树的方案。(不然就会有环)

在这里插入图片描述
在这里插入图片描述
原始的矩阵树定理是度-邻,边权形式是度数改为边权和,邻接情况改为边权,所求为生成树的边权之积的和。
Code:

#include<bits/stdc++.h>
#define maxn 55
using namespace std;
const int mod = 998244353;
int n,m,a[maxn][maxn],b[maxn][maxn],Y[maxn],f[maxn],len,ans;
inline int Pow(int a,int b){int s=1;for(;b;b>>=1,a=1ll*a*a%mod) b&1&&(s=1ll*s*a%mod);return s;}
int Det(int a[][maxn],const int N){
	int ret=1;
	for(int i=1;i<=N;i++){
		if(!a[i][i]) {for(int j=i+1;j<=N;j++) if(a[j][i]) {swap(a[i],a[j]),ret=-ret;break;}}
		if(!a[i][i]) return 0;
		for(int j=i+1;j<=N;j++) if(a[j][i]){
			int t=1ll*a[j][i]*Pow(a[i][i],mod-2)%mod;
			for(int k=i;k<=N;k++) a[j][k]=(a[j][k]-1ll*a[i][k]*t)%mod;
		}
		ret=1ll*ret*a[i][i]%mod;
	}
	return ret;
}
void Mul(int t){
	for(int i=len++;i>=0;i--) f[i]=((i?f[i-1]:0)-1ll*f[i]*t)%mod;
}
void Div(int t){//f[i]=g[i-1]-g[i]*t -> g[i]=f[i+1]+g[i+1]*t
	for(int i=--len,pre=0,now;i>=0;i--,pre=now) now=f[i],f[i]=(pre+1ll*f[i+1]*t)%mod;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=2,x;i<=n;i++) scanf("%d",&x),x++,a[x][i]=a[i][x]=-1,a[x][x]++,a[i][i]++;
	for(int x=0;x<n;x++){
		memcpy(b,a,sizeof a);
		for(int i=1;i<=n;i++) b[i][i]+=(n-1-a[i][i])*x;
		for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j&&!b[i][j]) b[i][j]-=x;
		Y[x]=Det(b,n-1);
	}
	f[0]=len=1;int D=1;
	for(int i=0;i<n;i++) Mul(i);
	for(int i=1;i<n;i++) D=1ll*D*(0-i)%mod; D=Pow(D,mod-2);
	for(int i=0;i<n;i++){
		Div(i); for(int j=0;j<=m;j++) ans=(ans+1ll*Y[i]*D%mod*f[j])%mod;
		Mul(i),D=1ll*D*(i-(n-1))%mod*Pow(i+1,mod-2)%mod;
	}
	printf("%d\n",(ans+mod)%mod);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值