【NOIP2014模拟8.24】小X 的二叉堆计数 (Standard IO) Time Limits: 1000 ms  Memory Limits: 262144 KB  Detailed Lim

 

Description

众所周知,完全二叉树是一种二叉树,满足除最后一层外的每层结点都是满的,且最后一层的结点连续集中在左方。而二叉堆是一种完全二叉树,分为大根堆和小根堆,大根堆满足父结点的值不小于子结点的值,小根堆满足父结点的值不大于子结点的值。
小X 最近对二叉堆和树的计数都很感兴趣,他想知道n 个互不相同的数能构成多少个不同的大小为n 的二叉堆,希望你帮帮他。

Input

第一行包含一个整数n。

Output

第一行包含一个整数,表示能构成的二叉堆个数模10^9 + 7。

Sample Input

3

Sample Output

4

Data Constraint

对于30% 的数据,n ≤ 10。
对于60% 的数据,n ≤ 1000。
对于80% 的数据,n ≤ 10^5。
对于100% 的数据,1 ≤ n ≤ 5 × 10^6。

Source / Author: 常州高级中学NOIP2014 heap

 

总结 : 30分暴力找规律, 没找出来。

原来是递推式很复杂。

可以设出某一限制下的方案数

 

题解 : 

我们f[i]表示有i个不同数字的小根堆有几种构成方案。

f

然后就处理阶乘及其逆元就行了。

 

#include<bits/stdc++.h>
#define open(x) freopen(x".in", "r", stdin);freopen(x".out", "w", stdout)
#define mem(a, b) memset(a, b, sizeof(a))
#define mcy(a, b) memcpy(a, b, sizeof(a))
#define pf printf
#define sf scanf
#define fo(i, a, b) for(register ll i = a; i <= b; ++i) 
#define fown(i, a, b) for(register ll i = a; i >= b; --i)
#define em(p, x) for(ll p=tail[x];p;p=e[p].fr)
#define ll long long
#define wt(a, c, d, s) fo(i, c, d) pf((s), a[i]); puts("")
#define rd(a, c, d) fo(iii, c, d) in((a)[i])
#define N 5000010
#define inf 2147483647
#define mod (ll)(1e9 + 7)
using namespace std;

template<class T> 
T in(T &x) {
	x=0;
	char ch = getchar(); ll f = 0;
	while(ch < '0' || ch > '9') f |= ch == '-', ch = getchar();
	while(ch >= '0' && ch <= '9') x = (x<<1) + (x<<3) + ch - '0', ch = getchar();
	x = f ? -x : x;
	return x;
}

ll n;
ll ans;
ll a[N], L[N], size[N];

#define ls (x << 1)
#define rs (x << 1 | 1)

ll fac[N], inv[N], f[N];
ll sqr(ll x) {return x * x % mod;}
ll power(ll x, ll p) {
	if(p == 0 )return 1;
	if(p & 1) return x * sqr(power(x, p / 2)) % mod; 
	return sqr(power(x, p / 2));
}

void init() {
	fac[0] = 1;
	fo(i, 1, n) fac[i] = fac[i - 1] * i % mod;
	inv[n] = power(fac[n], mod - 2);
	fown(i, n - 1, 0)
		inv[i] = inv[i + 1] * (i + 1) % mod; 
}


ll C(ll m, ll n) {
	return fac[m] * inv[n] % mod * inv[m - n] % mod;
}

void DFS(ll x) {
	ll S = size[x], l = L[x], r = size[x] - L[x] - 1;
	if(S == 2) {
		S = 2;
	}
	if(f[S])return ;
	f[S] = 1;

	if(ls > n) return ;
	
	DFS(ls);
	
	(f[S] *= C(S - 1, l))%=mod;
	(f[S] *= f[l])%=mod;
	
	if(rs > n) return ;
	
	DFS(rs);
	
	(f[S] *= f[r])%=mod;
	
}


void dfs(ll x) {
	size[x] = 1;
	if(ls > n) return ;
	dfs(ls);
	L[x] += size[ls];
	size[x] += size[ls];
	
	if(rs  > n) return ;
	dfs(rs);
	size[x] += size[rs];
}


int main() {
	in(n);
	if(n == 1 ) {
		pf("1\n");
		return 0;
	}
	
	dfs(1);
	
	init();
	
	DFS(1);
	
	pf("%lld\n", f[n] * 2 % mod);
	
	return 0;
}

O(n)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值