[ZJOI2010] 排列计数

题意:

称一个1-n的排列是Magic的,当且仅当$\forall i \in [2,n],p_i > p_{\lfloor \frac{i}{2} \rfloor}$。

求有多少排列是Magic的,答案对m取模。

$n\leq 10^{6},m\leq 10^{9}$。

 

题解:

容易发现这是一个完全二叉树的结构,要求每个点权大于父亲点权,求点权分配方案数。

那么直接dp即可,令$f_u$为以u为根的子树的方案数,则有$f_u = f_{2u}\times f_{2u+1}\times {{siz_u -1}\choose{siz_{2u}}}$。

注意数据范围并不满足$m>n$,所以需要$Lucas$定理算组合数。

复杂度$O(n)$。

 

套路:

  • 组合数取模时:系数不一定小于模数$\rightarrow$Lucas定理。

 

代码:

#include<bits/stdc++.h>
#define maxn 1000005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
#define rint register ll
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
ll n,mod,siz[maxn],dp[maxn],fac[maxn],ifac[maxn];

inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline ll power(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        a=a*a%mod,b>>=1;
    }
    return ans;
}
inline ll C(ll n,ll m){
    if(n>=mod) return C(n%mod,m%mod)*C(n/mod,m/mod)%mod;
    else return fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}

inline void dfs1(ll x){
    siz[x]=1;
    if((x<<1)<=n) dfs1(x<<1),siz[x]+=siz[x<<1];
    if((x<<1|1)<=n) dfs1(x<<1|1),siz[x]+=siz[x<<1|1];
}

inline void dfs2(ll x){
    if((x<<1)<=n && (x<<1|1)<=n){
        dfs2(x<<1),dfs2(x<<1|1);
        dp[x]=dp[x<<1]*dp[x<<1|1]%mod*C(siz[x]-1,siz[x<<1])%mod;
    }
    else if((x<<1)<=n) dfs2(x<<1),dp[x]=dp[x<<1];
    else if((x<<1|1)<=n) dfs2(x<<1|1),dp[x]=dp[x<<1|1];
    else dp[x]=1;
}

int main(){
    n=read(),mod=read();
    ll mx=min(n,mod-1); fac[0]=1; 
    for(ll i=1;i<=mx;i++) fac[i]=fac[i-1]*i%mod;
    ifac[mx]=power(fac[mx],mod-2);
    for(ll i=mx-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
    dfs1(1),dfs2(1);
    printf("%lld\n",dp[1]);
    return 0;
}
排列计数

 

posted @ 2020-07-03 10:47  Fugtemypt  阅读(121)  评论(0编辑  收藏  举报