题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2111
题解:
有一步非常妙的转化,就是可以将这个序列转移到二叉树上。
问题变成:要求除
1
1
1以外的结点中
i
i
i号结点一定比
i
i
i号结点的父亲的权值要大,问有多少种方案
显然可以树形dp,令
d
p
[
i
]
dp[i]
dp[i]表示以
i
i
i号结点为父节点子树满足条件的个数
则
d
p
[
i
]
=
d
p
[
i
<
<
1
]
∗
d
p
[
i
<
<
1
∣
1
]
∗
C
(
t
r
e
e
[
i
]
−
1
,
t
r
e
e
[
i
<
<
1
]
)
dp[i]=dp[i<<1]*dp[i<<1|1]*C(tree[i]-1,tree[i<<1])
dp[i]=dp[i<<1]∗dp[i<<1∣1]∗C(tree[i]−1,tree[i<<1])
解释:后面乘的那个组合数是因为两棵子树之间的树可以任意选(对
i
i
i结点没有影响)
组合数需要用
l
u
c
a
s
lucas
lucas定理算,因为可能算组合数的时候
C
(
n
,
m
)
m
o
d
  
p
C(n,m) \mod p
C(n,m)modp,
n
n
n比
p
p
p大,这就萎了。
一组数据:4 2 ans=1
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const int maxn=2e6+5;
int mod,n;
int fac[maxn],inv[maxn];
int dp[maxn],tree[maxn];
int pw(int x,int y){
if(!y)return 1;if(y==1)return x%mod;
int mid=pw(x,y>>1);
if(y&1)return 1ll*mid*mid%mod*x%mod;
else return 1ll*mid*mid%mod;
}
int C(int x,int y){
return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int lucas(int x,int y){
if(y==0)return 1;
return 1ll*C(x%mod,y%mod)*lucas(x/mod,y/mod)%mod;;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=1000000;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv[1]=1;
for(int i=2;i<=1000000;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod; // 注意这里求的是单个数的逆元,而组合数要用的是阶乘的逆元
for(int i=2;i<=1000000;i++)inv[i]=1ll*inv[i]*inv[i-1]%mod;
}
int main(){
scanf("%d%d",&n,&mod);
init();
for(int i=n;i>=1;i--){
tree[i]=tree[i<<1]+tree[i<<1|1]+1;
if((i<<1)>n)dp[i]=1;
else{
if((i<<1|1)>n)dp[i]=dp[i<<1];
else{
dp[i]=1ll*dp[i<<1]*dp[(i<<1|1)]%mod*lucas(tree[i]-1,tree[i<<1])%mod;
}
}
}
printf("%d\n",dp[1]);
// for(int i=1;i<=n;i++)
// printf("%d %d %d %d\n",dp[i],tree[i],tree[i<<1],C(tree[i]-1,tree[i<<1]));
return 0;
}