(Prufer序列)牛客挑战赛48C.铬合金之声

博客介绍了如何使用动态规划和Prufer序列解决一个涉及森林中无根树权值计算的问题。通过将无根树转换为有根树并利用组合数学的知识,可以高效地计算出总方案数并在O(m)时间内求出答案。代码部分展示了实现这个算法的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

牛客挑战赛48C.铬合金之声

思路:

首先由题很显然的一个结论就是,这是由 n n n个点构成的有 n − m n-m nm棵无根树的森林。
考虑权值的计算。假设第 i i i棵树的大小为 s i s_i si,权值就为 ∏ i = 1 n − m s i \prod\limits_{i=1}^{n-m}s_i i=1nmsi。但是这样的话,又要考虑乘上方案数,并且每种方案都需要分开考虑。很巧妙的一步就是,将无根树转换为有根树,因为有根树的方案数就等于无根树的方案数乘上结点数,恰好是我们需要的贡献。
所以转换成了求 n n n个点构成 n − m n-m nm棵有根树的方案数。
这一步也很巧妙。我们设一个虚根 0 0 0,令其度数为 n − m n-m nm。通过Prufer序列的定义,我们可以发现 0 0 0会在长度为 n − 1 n-1 n1的序列种出现 n − m − 1 n-m-1 nm1次,所以它的方案数为 ( n − 1 n − m − 1 ) = ( n − 1 m ) \begin{pmatrix}n-1\\n-m-1\end{pmatrix}=\begin{pmatrix}n-1\\m\end{pmatrix} (n1nm1)=(n1m)。剩下的 m m m个位置中,每个位置都可以从 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n中任选一个数,所以方案数为 n m n^m nm。通过乘法原理,总的方案数为 ( n − 1 m ) n m \begin{pmatrix}n-1\\m\end{pmatrix}n^m (n1m)nm
( n − 1 m ) = ( n − 1 ) ! m ! ( n − 1 − m ) ! \begin{pmatrix}n-1\\m\end{pmatrix}=\cfrac{(n-1)!}{m!(n-1-m)!} (n1m)=m!(n1m)!(n1)!,但是 n n n比较大,线性计算会T。所以稍微转换一下 ( n − 1 m ) = ( n − 1 ) m ‾ m ! \begin{pmatrix}n-1\\m\end{pmatrix}=\cfrac{(n-1)^{\underline{m}}}{m!} (n1m)=m!(n1)m
公式的说明:
x n ‾ = { 1 n = 0 x ( x − 1 ) n − 1 ‾ n ≥ 1 x^{\underline{n}}=\begin{cases}1&n = 0 \\x(x-1)^{\underline{n-1} }&n\ge1\end{cases} xn={1x(x1)n1n=0n1
这样就可以在 O ( m ) O(m) O(m)的时间内求出答案了。

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define int long long
#define cl(x,y) memset(x,y,sizeof(x))
#define loop(x,y,z) for(x=y;x<=z;x++)
#define reve(x,y,z)	for(x=y;x>=z;x--)
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define all(x) x.begin(),x.end()
#define lson x<<1,l,mid
#define rson x<<1|1,mid+1,r
#define INF 1e18
const int N=1e6+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const double eps=1e-8;
const double pi=acos(-1);
using namespace std;
int qpow(int a,int b)
{
	a%=mod;
	int ans=1;
	while(b)
	{
		if(b&1)
			ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,m,i;
	cin>>n>>m;
	int ans=1,tot=1;
	for(i=1;i<=m;i++)
	{
		ans=ans*(n-i)%mod;
		tot=tot*(m-i+1)%mod;
	}
	ans=ans*qpow(tot,mod-2)%mod*qpow(n,m)%mod;
	cout<<ans<<endl;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值