1. 程式人生 > >BZOJ 1485: [HNOI2009]有趣的數列(卡特蘭數)

BZOJ 1485: [HNOI2009]有趣的數列(卡特蘭數)

傳送門

解題思路

  因為總共是一個排列,那麼確定了奇數項是哪些,偶數項就確定了。怎麼判斷奇數項是否合法呢,其實由於第三個限制,可以把這個數列看成一個括號序列,奇數項為\((\),偶數項為\()\),那麼合法方案數自然是卡特蘭數了。。模數不是質數,而且\(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;   
}