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; }