牛客挑战赛48C 铬合金之声【Prufer序列+思维转化】

题目链接

题意在这里插入图片描述

分析

n n n 个点中加入 m m m 条边,形成 n − m n-m nm 个联通块,那么每个联通块必然为一棵树。利用反证法可以证明:如果一个大小为 x x x 的联通块含有大于 x − 1 x-1 x1 条边,那么最后形成的联通块的数量显然小于 n − m n-m nm 个,因为有一些边并没有使得联通块的数量减少 1 1 1
又因为,对于一棵无根树而言,计算其大小的贡献,相当于选择其根的方案数。此时,再建立一个虚点 0 0 0 并固定其度为 n − m n-m nm ,就将 n − m n-m nm 棵树的森林转化为 n + 1 n+1 n+1 个节点的树。我们要求的就是以 0 0 0 为根的树的个数,可以借助 Prufer 序列来解决。因为 0 0 0 号点的度固定为 n − m n-m nm,那么它在序列中出现的次数为 n − m − 1 n-m-1 nm1,剩余的 n − 1 − ( n − m ) n-1-(n-m) n1(nm) 个位置为 [ 1 , n ] [1,n] [1,n]任意选,所以最终的方案数为:
( n − 1 n − m − 1 ) ⋅ n m = ( n − 1 m ) ⋅ n m \left(\begin{matrix} n-1\\ n-m-1 \end{matrix}\right)·n^m= \left(\begin{matrix} n-1\\ m \end{matrix}\right)·n^m (n1nm1)nm=(n1m)nm
( n − 1 m ) \left(\begin{matrix} n-1\\m\end{matrix}\right) (n1m) 可以通过公式: ( n − 1 ) ⋯ ( n − m ) m ⋅ ( m − 1 ) ⋅ ⋯ 1 \frac{(n-1)\cdots (n-m)}{m·(m-1)·\cdots 1} m(m1)1(n1)(nm)和递推逆元求出, n m n^m nm 使用快速幂直接求解。最终的复杂度: O ( m ) O(m) O(m)

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=1e7+5;
int inv[N];
ll power(ll x,ll y){
	ll res=1;
	x%=mod;
	while(y){
		if(y&1) res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	ll res=1;
	for(int i=0;i<m;i++)
		res=res*(n-1-i)%mod;
	inv[1]=1;
	for(int i=2;i<=m;i++)
		inv[i]=(mod-1LL*mod/i*inv[mod%i]%mod)%mod;
	for(int i=1;i<=m;i++) res=res*inv[i]%mod;
	res=res*power(1LL*n,1LL*m)%mod;
	printf("%lld\n",res);
	return 0;
}
//C(n-1,m)*n^m
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值