您的位置:首页 > 其它

[BZOJ2111][ZJOI2010]Perm排列计数(组合数学)

2018-07-09 19:39 337 查看

题意就是求一个n个点的堆的合法形态数。

显然,给定堆中所有数的集合,则这个堆的根是确定的,而由于堆是完全二叉树,所以每个点左右子树的大小也是确定的。

设以i为根的堆的形态数为F(i),所以F(i)+=F(sz[2*i])*F(sz[2*i+1])*C(sz[i]-1,sz[2*i])。直接DP即可。

有个令人无语的坑,n可能大于p,要用Lucas。

还有求阶乘逆元的时候根本不需要用快速幂算出fac
的逆元再逆推回去,直接跟阶乘一样顺推就好了。

#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
using namespace std;

const int N=1000010;
int n,p,fac
,inv
,Fin
,s
,f
;

int C(int n,int m){
if (n<m) return 0;
if (n<p && m<p) return 1ll*fac
*Fin[m]%p*Fin[n-m]%p;
return 1ll*C(n/p,m/p)*C(n%p,m%p)%p;
}

int main(){
freopen("bzoj2111.in","r",stdin);
freopen("bzoj2111.out","w",stdout);
scanf("%d%d",&n,&p); int m=min(n,p);
fac[0]=1; rep(i,1,m) fac[i]=1ll*fac[i-1]*i%p;
inv[1]=1; rep(i,2,m) inv[i]=1ll*(p-p/i)*inv[p%i]%p;
Fin[0]=1; rep(i,1,m) Fin[i]=1ll*Fin[i-1]*inv[i]%p;
for (int i=n; i; i--){
s[i]=s[i<<1]+s[(i<<1)|1]+1;
f[i]=1ll*((i<<1)>n?1:f[i<<1])*((i<<1|1)>n?1:f[i<<1|1])%p*C(s[i]-1,s[i<<1])%p;
}
printf("%d\n",f[1]);
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: