BZOJ 1485: [HNOI2009]有趣的數列(卡特蘭數)
阿新 • • 發佈:2018-12-04
解題思路
因為總共是一個排列,那麼確定了奇數項是哪些,偶數項就確定了。怎麼判斷奇數項是否合法呢,其實由於第三個限制,可以把這個數列看成一個括號序列,奇數項為\((\),偶數項為\()\),那麼合法方案數自然是卡特蘭數了。。模數不是質數,而且\(n^2\)會超時,就只能用\(ans=\frac{\dbinom{2n}{n}}{n+1}\)這個式子了,化簡一下就是\(ans=\dfrac{\prod\limits_{i=n+2}^{2n}i}{\prod\limits_{i=1}^ni}\),然後篩一下質數,把這個式子拆成若干個素數次冪乘積形式,然後快速冪就能做了。
程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int MAXN = 2000005; typedef long long LL; int n,MOD,cnt[MAXN],tot,prime[MAXN]; int vis[MAXN],ans=1; inline int fast_pow(int x,int y){ int ret=1; for(;y;y>>=1){ if(y&1) ret=(LL)ret*x%MOD; x=(LL)x*x%MOD; } return ret; } int main(){ scanf("%d%d",&n,&MOD);vis[1]=1; for(int i=2;i<=2*n;i++){ if(!vis[i]) vis[i]=i,prime[++tot]=i; for(int j=1;j<=tot && (LL)i*prime[j]<=2*n;j++){ vis[i*prime[j]]=prime[j]; if(!(i%prime[j])) break; } } for(int i=2;i<=n;i++) cnt[i]=-1; for(int i=n+2;i<=n*2;i++) cnt[i]=1; for(int i=n*2;i>1;i--) { if(vis[i]==i) ans=((LL)ans*fast_pow(i,cnt[i])%MOD); else cnt[vis[i]]+=cnt[i],cnt[i/vis[i]]+=cnt[i]; } printf("%d\n",ans); return 0; }