洛谷P2606
点我看原题
题意:给出两个正整数n,m,求1~n所有满足
∀
i
,
A
i
/
2
<
A
i
\forall i,A_{i/2}<A_i
∀i,Ai/2<Ai的排列数(对m取模)
对于任意一个满足的序列可以写成小根堆的形式。
想递归求解,
定
义
f
(
x
)
为
x
个
数
字
能
形
成
满
足
条
件
的
序
列
数
定义f(x)为x个数字能形成满足条件的序列数
定义f(x)为x个数字能形成满足条件的序列数
假
设
左
右
子
树
上
分
别
有
l
e
,
r
i
个
节
点
假设左右子树上分别有le,ri个节点
假设左右子树上分别有le,ri个节点
那
么
f
(
x
)
=
C
n
−
1
l
e
∗
f
(
l
e
)
∗
f
(
r
i
)
那么f(x)=C^{le}_{n-1}*f(le)*f(ri)
那么f(x)=Cn−1le∗f(le)∗f(ri)
由于m可能小于n,求组合数用Lucas定理。
#include<bits/stdc++.h>
#define maxn 1000005
#define LL long long
LL n,m,f[maxn],fac[maxn],inv[maxn];
LL C(LL n,LL m,LL p){
if(m<=0)return 1;
LL nn=n%p,mm=m%p;
if(nn<mm) return 0;
return fac[nn]*inv[mm]%p*inv[nn-mm]%p*C(n/p,m/p,p)%p;
}
LL ksm(LL a,LL b,LL p){
LL w=a,s=1;
while(b){
if(b&1)s=s*w%p;
w=w*w%p;
b>>=1;
}
return s;
}
int main(){
scanf("%lld%lld",&n,&m);
fac[0]=1;
int t=n<m?n:m;
if(t==m)t--;
for(int i=1;i<=t;++i){
fac[i]=fac[i-1]*i%m;
}
inv[t]=ksm(fac[t],m-2,m);
for (int i=t-1;i>=1;--i){
inv[i]=inv[i+1]*(i+1)%m;
}
inv[0]=1;
f[0]=f[1]=1;
LL le,ri;
le=ri=0;
for(int i=2;i<=n;++i){
int k=log(i)/log(2);
if(i-(1<<k)-(1<<k-1)>=0)ri++;
else le++;
f[i]=C(i-1,ri,m)*f[le]%m*f[ri]%m;
}
printf("%lld\n",f[n]%m);
return 0;
}