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