2111: [ZJOI2010]Perm 排列计数
Time Limit: 10 Sec Memory Limit: 259 MBDescription
称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
Input
输入文件的第一行包含两个整数 n和p,含义如上所述。
Output
输出文件中仅包含一个整数,表示计算1,2,⋯, n的排列中, Magic排列的个数模 p的值。
Sample Input
20 23
Sample Output
16
HINT
100%的数据中,1 ≤ N ≤ 106, P ≤ 10^9,p是一个质数。 数据有所加强
Source
Tips:
数据有所加强,因为N比较小,可以用lucas定理
树形dp
dp[i]=dp[i*2]*dp[i*2+1]*C(size[i]-1,size[i*2])%P;
Code:
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; long long n,m,p,size[4000008],dp[4000008],f[4000008]; long long ksm(long long a,long long b,long long MOD){ long long res=1; while(b>0){ if(b%2==1) res=a*res%MOD; b=b/2; a=a*a%MOD; } return res; } void init(long long MOD){ f[0]=1; for(int i=1;i<=n;i++) f[i]=f[i-1]*i%MOD; } long long lucas(long long a,long long b,long long MOD){ long long res=1; while(a&&b){ long long aa=a%MOD,bb=b%MOD; res=res*f[aa]*ksm(f[aa-bb]*f[bb]%MOD,MOD-2,MOD)%MOD; a=a/MOD; b=b/MOD; } return res; } int main(){ scanf("%lld%lld",&n,&p); for(int i=1;i<=2*n;i++) dp[i]=1; init(p); for(int i=n;i>0;i--){ size[i]++; size[i/2]+=size[i]; } for(int i=n;i>0;i--){ if(size[i]==1) continue; else{ dp[i]=dp[i*2]*dp[i*2+1]%p*lucas(size[i]-1,size[i*2],p)%p; } } printf("%lld",dp[1]); }