码迷,mamicode.com
首页 > 其他好文 > 详细

[ZJOI2010]排列计数

时间:2019-10-16 19:27:18      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:zjoi   init   pow   组合   for   using   size   out   inline   

题意

求1~n的全排列\(P_i\)的个数,满足对于\(i\geq 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]排列计数

标签:zjoi   init   pow   组合   for   using   size   out   inline   

原文地址:https://www.cnblogs.com/Chtholly/p/11687740.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!