P2606 [ZJOI2010] 排列计数

//URL:https://www.luogu.com.cn/problem/P2606
/*
称一个 1∼n1∼n 的排列 p1,p2,…,pnp1​,p2​,…,pn​ 是 Magic 的,当且仅当
∀i∈[2,n],pi>p⌊i/2⌋
∀i∈[2,n],pi​>p⌊i/2⌋​
计算 1∼n1∼n 的排列中有多少是 Magic 的,答案可能很大,只能输出模 mm 以后的值。

p[i]>p[i>>1]:完全二叉树
dp[i]:i个结点的完全二叉树形态数
    dp[i]=C(i-1,lft)*dp[lft]*dp[i-1-lft] 
    
C(n,m)%p==C(n/p,m/p)*C(n%p,m%p)%p

inv[i]=(p-p/i)*inv[p%i]%p 
fac[i]:i!
vol[i] 编号i的结点有 多少结点

::m不一定>n->Lucas
*/
/*
20 23 
16

*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string.h>
#include<queue>
#include<vector>
#include<bits/stdc++.h>
typedef long long ll;
#define ddd printf("-----------------------\n");
using namespace std;
const int maxn=1e1 +10;
const int mod=998244353;
const int inf=0x3f3f3f3f;

ll n,p,siz;
ll dp[1000010],fac[1000010],inv[1000010],vol[1000010];

ll ksm(ll a,ll b){//
    ll res=1;
    while(b)
    {
        if(b%2==1) res=res*a%p;
        a=a*a%p,b>>=1;
    }
    return res;
}

ll Lucas(int n,int m)
{
    if(m>n) return 0;
    if(n<p&&m<p) return fac[n]*inv[m]%p*inv[n-m]%p;
    return Lucas(n/p,m/p)*Lucas(n%p,m%p)%p;
}

int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>p; siz=min(p-1,n);//siz<=p-1
    fac[0]=1;
    for(int i=1;i<=siz;i++) fac[i]=fac[i-1]*i%p;
    inv[siz]=ksm(fac[siz],p-2);
    for(int i=siz-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%p;
    
    for(int i=n;i>=1;i--)
    {
        vol[i]=1;if(i*2<=n) vol[i]+=vol[i*2];if(i*2+1<=n) vol[i]+=vol[i*2+1];
        if(i*2+1<=n) dp[i]=(dp[i*2]*Lucas(vol[i]-1,vol[i*2])%p*dp[i*2+1])%p;
          else if(i*2<=n) dp[i]=dp[i*2];
          else dp[i]=1;
    }
    cout<<dp[1]<<'\n';
    
    return 0;
}

 

posted @ 2024-02-07 11:19  JMXZ  阅读(27)  评论(0)    收藏  举报