[ZJOI2010]排列计数
Posted chtholly
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2010]排列计数相关的知识,希望对你有一定的参考价值。
题意
求1~n的全排列(P_i)的个数,满足对于(igeq 2),有(P_i > P_{i/2})
思路
随手画个图就可以发现问题是求大小为(n)的小根堆的个数
由于左右子树互不影响,直接DP即可,设(dp_{i})表示以(i)为根的小根堆的个数,有(dp_i = dp_{i*2} * dp_{i*2+1} * C(size_i - 1 , size_{i*2}))
注意本题模数可能小于(n),所以要用(Lucas)定理求组合数
Code
#include<bits/stdc++.h>
#define N 2000005
#define Min(x,y) ((x)<(y)?(x):(y))
using namespace std;
typedef long long ll;
int n,size[N];
ll mod;
ll jc[N],inv[N],dp[N];
ll quickpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1) ret=ret*a%mod;
a=a*a%mod;
b>>=1;
}
return ret;
}
void init()
{
jc[0]=1;
int t=Min(n,mod-1);
for(int i=1;i<=t;++i) jc[i]=jc[i-1]*i%mod;
inv[t]=quickpow(jc[t],mod-2);
for(int i=t-1;i>=0;--i) inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(int n,int m) {return n>=m ? jc[n]*inv[m]%mod*inv[n-m]%mod : 0;}
ll lucas(int n,int m)
{
if(!m) return 1;
return C(n%mod,m%mod)*lucas(n/mod,m/mod)%mod;
}
void DFS(int rt)
{
if(rt>n) return;
size[rt]=1;
DFS(rt<<1); DFS(rt<<1|1);
size[rt]+=size[rt<<1]+size[rt<<1|1];
}
void dfs(int rt)
{
if(rt>n) return (void)(dp[rt]=1);
dfs(rt<<1); dfs(rt<<1|1);
dp[rt]=lucas(size[rt]-1,size[rt<<1])*dp[rt<<1]%mod*dp[rt<<1|1]%mod;
}
int main()
{
cin>>n>>mod;
init();
DFS(1);
dfs(1);
cout<<dp[1]<<endl;
return 0;
}
以上是关于[ZJOI2010]排列计数的主要内容,如果未能解决你的问题,请参考以下文章