ZOJ - 4069(2018 青岛现场赛 L) - 指数型生成函数

题目链接:https://vjudge.net/problem/ZOJ-4069

 

解题思路:

1.n个点组成环的不同种类数是(n-1)!/2;n个点组成一条链的不同种类数是n!/2,特别的n==1时种类数为1。

用指数型生成函数表示k个点形成链的种类: 1/2(2x+2!*x^2/2!+3!*x^3/3!+4!*x^4/4!+..+n!*x^n/n!) = 1/2(2*x+x^2+x^3+x^4+..+x^n) = S

x*S = 1/2(2x^2+x^3+x^4+..+x^(n+1)),x*S - S = 1/2(x^2+x^(n+1)-2*x),S = 1/2*(x^2+x^(n+1)-2*x)/(x-1) = 1/2*x*(x+x^n-2)/(x-1)。

当|x|<1时,n->∞时,x^n->0,S = 1/2*x*(x-2)/(x-1) = x*(1-x/2)/(1-x)

同理用上诉方法当|x|<1,n->∞时可以求得1+x+x^2+x^3+...+x^n = 1/(1-x)。(这个不就是上面式子的分母吗?下面会说他的作用)

2.如果在n个点中只给你m条边,那么最后形成的图肯定是由n-m条链组成的,令k = n - m。

3.那么要求n个点组成k条链的不同方案数就是求S^k = (x*(1-x/2)/(1-x))^k的多项式展开的第n项的系数,也就是a[n]*x^n/n!,a[n]就是我们要求的,不过最后还要除以k!,因为k条链不需要分先后顺序。

4.S^k = (x*(1-x/2)/(1-x))^k = x^k*(1-x/2)^k*(1/(1-x))^k,此处(1-x/2)^k可以用二项式展开,那么1/(1-x)该如何处理呢,这就用到了上面已经求出的1/(1-x)的多项式,这个多项式所以系数都是1,那么与此多项式相乘得到第n项的系数不就是原多项式的0到n项系数的和吗?

所以(1-x/2)^k的多项式乘一次1/(1-x)得到新的系数,就是求n前系数前缀和,乘k次就是求k次。

5.在k次求前缀和中我们可以直接算出原多项式((1-x/2)^k)的每一项系数对最终结果的贡献。此处列举第1项的系数的贡献T,当k=1时显然T = 1,k = 2时此时所有1到n的项第1项都加上了一次,所以T = k,这个结果就可以想到用组合求解。所以对于求k次前缀和之后原第i项对最终第j项(j>=i)的贡献次数是C(j-i+k-1,k-1)。可以用(C(m,m)+C(m+1,m)+C(m+2,m)+C(m+3,m)+...+C(n,m)==C(n+1,m+1))去推。

6.最后记得乘上(1-x/2)^k上的-1/2的幂啊!求个逆元就行了,还有此多项式的第i项系数就是C(k,i)*(-1/2)^i。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int mod = 1e9 + 7;
const int mx = 1e5 + 10;
ll fac[mx],inv[mx];
ll n ,m;
void init()
{
	inv[0] = fac[0] = inv[1] = 1;
	for(int i=1;i<mx;i++) fac[i] = fac[i-1]*i%mod;
	for(int i = 2;i<mx;++i) inv[i] = (mod-mod/i)*inv[mod%i] % mod;
	for(int i=2;i<mx;i++) inv[i] = inv[i]*inv[i-1]%mod;
}
ll C(int x,int y)
{
	return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main()
{
	int t;
	init();
	scanf("%d",&t);
	while(t--){
		scanf("%lld%lld",&n,&m);
		if(m>n){
			puts("0");
			continue;
		}
		if(m==n){
			printf("%lld\n",fac[n-1]*inv[2]%mod);
			continue;
		}
		ll ans = 0,base = 1;
		int k = n - m,cnt = min(m,n-m);
		for(int i=0;i<=cnt;i++){
			ans = (ans + C(k,i)*base%mod*C(m-i+k-1,k-1))%mod;
			base = base * -inv[2]%mod;
		}
		ans = (ans+mod)%mod;
		printf("%lld\n",ans*fac[n]%mod*inv[n-m]%mod); 
	}
	return 0;
} 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值