题目描述
题解
首先第一个位置肯定是1
若把i的两个儿子看做i*2和i*2+1,这样就形成了一颗以1为根的有根树
这棵树的形态是不变的,我们需要做的就是将1-n填到每一个节点里然后保证父亲小于儿子
对于一颗子树,我们考虑怎样选才能满足要求,可以发现由于数是1-n,两两数之间的相对大小是不变的,也就是说,不会出现这棵子树中可以填2,3,4而不能填3,4,5的情况
而假设我们选出了若干数填到这棵子树中,根一定是确定的,也就是这些数中最小的数
那么选数的方案只由子树的大小有关
可以写出递推式f(i)=c(size(i)-1,size(ls(i)))*f(i*2)*f(i*2+1),c是组合数
递归求解即可
不过还有一个问题就是,这里可能n>p导致np不互质,这样求组合数不能直接用逆元,要用lucas定理
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define LL long long
#define N 1000005
LL n,Mod,ans=1LL;
LL size[N+1],mul[N+1];
void calc()
{
mul[0]=1LL;
for (LL i=1;i<=n;++i) mul[i]=mul[i-1]*i%Mod;
}
void exgcd(LL a,LL b,LL &x,LL &y)
{
if (!b) x=1LL,y=0LL;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
LL inv(LL A,LL Mod)
{
LL a=A,b=Mod,x=0LL,y=0LL;
exgcd(a,b,x,y);
x=(x%b+b)%b;
if (!x) x+=b;
return x;
}
LL C(LL n,LL m)
{
if (m>n) return 0LL;
return mul[n]*inv(mul[m]*mul[n-m]%Mod,Mod)%Mod;
}
LL lucas(int n,int m,int Mod)
{
LL ans=1LL;
for (;m;n/=Mod,m/=Mod)
ans=ans*C(n%Mod,m%Mod)%Mod;
return ans;
}
void dfs(LL x)
{
LL l=0;
if (x*2<=n)
{
dfs(x*2);
size[x]+=size[x*2];
l=size[x*2];
}
if (x*2+1<=n)
{
dfs(x*2+1);
size[x]+=size[x*2+1];
}
ans=ans*lucas(size[x],l,Mod)%Mod;
++size[x];
}
int main()
{
scanf("%lld%lld",&n,&Mod);
calc();
dfs(1);
printf("%lld\n",ans);
}
总结
①遇到求逆元一定要考虑ap是否互质