二叉堆题解(分块打表)

题目大意

二叉堆是一种特殊的堆,其同时满足完全二叉树和堆的性质。

求有多少种不同的 n n n个点的二叉堆满足所有节点的权值都在 1 1 1 n n n之间且互不相同。输出答案对 1 0 9 + 7 10^9+7 109+7取模。


数据范围

1 ≤ n ≤ 1 0 9 1\leq n\leq 10^9 1n109


题解

s i s_i si表示子树 i i i的大小, l i l_i li r i r_i ri分别表示 i i i的左儿子和右儿子。

g t ( i ) gt(i) gt(i)表示以 i i i为根的子树内不重复地填上 1 1 1 s i s_i si的方案数。则有

g t ( i ) = C s i − 1 s l i g t ( l i ) × g t ( r i ) gt(i)=C_{s_i-1}^{s_{l_i}}gt(l_i)\times gt(r_i) gt(i)=Csi1sligt(li)×gt(ri)

把组合数拆开后,经过整理,我们可以发现答案就是 n ! ∏ i = 1 n s i \dfrac{n!}{\prod\limits_{i=1}^ns_i} i=1nsin!。那么,可以先求 1 ∏ i = 1 n s i \dfrac{1}{\prod\limits_{i=1}^ns_i} i=1nsi1,然后再乘上 n ! n! n!

对于一棵完全二叉树,根节点的左右子树中至少有一棵为满二叉树。

f i f_i fi表示子树大小为 2 i − 1 2^i-1 2i1的点的 s s s值之积,那么有

f i = f i − 1 × f i − 1 2 i − 1 f_i=\dfrac{f_{i-1}\times f_{i-1}}{2^i-1} fi=2i1fi1×fi1

那么,我们可以从根节点开始,先找到是满二叉树的子树,乘上它的贡献,然后递归到另一个子树中继续求值。

这样我们就可以 O ( log ⁡ n ) O(\log n) O(logn)求出 1 ∏ i = 1 n s i \dfrac{1}{\prod\limits_{i=1}^ns_i} i=1nsi1

那么,怎么求 n ! n! n!呢?分段打表即可。设分了 k k k段,那么可以 O ( 1 ) O(1) O(1)得出 ⌊ n − 1 k ⌋ + 1 \lfloor\dfrac{n-1}{k}\rfloor+1 kn1+1的阶乘,然后 O ( 1 0 9 k ) O(\dfrac{10^9}{k}) O(k109)求出 n n n的阶乘。

时间复杂度为 O ( log ⁡ n + 1 0 9 k ) O(\log n+\dfrac{10^9}{k}) O(logn+k109)

code

#include
using namespace std;
long long ans,f[35];
long long t[100]={1,924724006,582347126,500419162,881147799,693776109,435873621,279027658,727951124,398578768,
				678364145,204828554,345795998,116118093,359401113,236930793,856493327,207383191,617606889,933753281,
				26701748,329394893,360779992,416008308,187501984,165706817,328891607,16385287,117411011,404196042,
				765064133,239669664,761588352,566114869,673499119,840260100,352356536,53839501,178657924,373444237,
				227300165,207172723,444208499,367531373,297449176,605324209,729265513,567907756,125889461,250743107,
				666666670,598576559,632705086,295855233,185718228,414607857,737215408,863388390,182290465,707552496,
				881713600,417895708,490627919,364521407,775935292,972492338,473340273,920880265,530581,696910290,
				64037482,649527920,756691728,283805222,711255329,825205499,263679166,341083474,914727729,919247968,
				465317279,960145703,274813468,393588827,65909169,521964827,794328994,484551338,521297378,54488990,
				591837535,255746228,25827429,177799409,92011129,469664591,35708489,197025781,288851931,254032854};
long long mod=1000000007;
long long mi(long long t,long long v){
	if(!v) return 1;
	long long re=mi(t,v/2);
	re=re*re%mod;
	if(v&1) re=re*t%mod;
	return re;
}
long long gt(int n){
	int x;
	long long re;
	for(x=1;(1<<x)-1<n;x++);
	if((1<<x)-1==n) return f[x];
	if(n<=(1<<x)-1-(1<<x-2)) re=f[x-2]*gt(n-(1<<x-2))%mod;
	else re=f[x-1]*gt(n-(1<<x-1))%mod;
	re=re*mi(n,mod-2)%mod;
	return re;
}
int main()
{
	int n;
	scanf("%d",&n);
	f[0]=1;
	for(int i=1;i<=30;i++) f[i]=f[i-1]*f[i-1]%mod*mi((1<<i)-1,mod-2)%mod;
	ans=gt(n)*t[(n-1)/10000000]%mod;
	for(int i=(n-1)/10000000*10000000+2;i<=n;i++) ans=ans*i%mod;
	printf("%lld",ans);
	return 0;
}

你可能感兴趣的:(题解,c++,题解)