称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
输入文件的第一行包含两个整数 n和p,含义如上所述。
输出文件中仅包含一个整数,表示计算1,2,⋯, ���的排列中, Magic排列的个数模 p的值。
100%的数据中,1 ≤ ��� N ≤ 106, P��� ≤ 10^9,p是一个质数。 数据有所加强
完全二叉树+dp
如果对于有大小要求的点连边的话会形成一棵完全二叉树。
f[i]表示树中有i个点的方案数。l是左子树结点个数,r是右子树结点个数。
f[i]=f[l]*f[r]*C(i-1,l)
因为对于左右子树的大小是没有要求的,因此要先从i-1个中选出l个来分配给左子树。
对于l和r的求法,画一个图就很好得到了(详见程序中Getl(x))。
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <cstdlib> #define LL long long #define M 1000000+5 using namespace std; LL ans[M],fac[M],inv[M]; int n,m,mod; LL Pow(LL a,int n) { LL ans=1LL,base=a; while (n) { if (n&1) ans=ans*base%mod; base=base*base%mod; n>>=1; } return ans; } void Prepare() { fac[0]=inv[0]=1LL; for (int i=1;i<=n;i++) { int x=i; while (x%mod==0) x/=mod; fac[i]=fac[i-1]*(LL)x%mod; } for (int i=1;i<=n;i++) inv[i]=Pow(fac[i],mod-2); } LL Getc(int n,int m) { if (n<m) return 0LL; return fac[n]*inv[m]%mod*inv[n-m]%mod; } int Getl(int x) { int b=1; for (int i=1;;i++) { b*=2; if (x<b) break; } b/=2; return min(b/2,x-1-(b/2-1)*2)+b/2-1; } LL dfs(int n) { if (ans[n]) return ans[n]; int l=Getl(n); return ans[n]=Getc(n-1,l)*dfs(l)%mod*dfs(n-1-l)%mod; } int main() { scanf("%d%d",&n,&mod); Prepare(); ans[1]=ans[0]=1LL%mod; printf("%lld\n",dfs(n)); return 0; }
感悟:
为什么会WA呢?是Prepare()中求阶乘的问题影响到了求逆元!!
如果阶乘中%mod=0,那么就无法求出逆元了!因此求阶乘遇到mod的倍数要先/mod!!!