P3200 [HNOI2009]有趣的数列

呜呜呜,本蒟蒻第一次做卡特兰数,实在是太菜了。写一个题解加深记忆。

Solution

首先,题里面说明 \(a_2<a_4< \cdots <a_{2n}\)\(a_{2i-1}<a_{2i}\) ,可得偶数位上的数比它前面任意一个数都要大

那么我们可以将题意转化为:将 \(1\) ~ \(2n\) 放入这个数列,每次放的数要么放在最前面的奇数位要么放在最前面的偶数位。

你觉得我说的可能是废话

现在说另一个结论:一个数列填入偶数位的个数要时刻不超过填入奇数位的个数

证明:设放在了偶数位上 \(a\) 个数,放在奇数位上 \(b\) 个数,并且 \(a>b\) ,那么现在放了 \(a+b\) 个数,显然 \(a>\frac x2=\frac {a+b}2\) ,并且现在最后一个偶数位的下标\(2\times a\) ,但是 \(2\times a>x\) ,也就是说不管你在这一位放啥都是不满足条件的。

现在答案就是满足任意位偶位数不超过奇位数的数列个数

\(f_{i,j}\) 表示填了 \(i\) 个偶数位, \(j\) 个奇数位的方案数。递推方程就是 \(f_{i,j}=f_{i-1,j}+f_{i,j-1}\)

最后的答案就是 \(f_{n,n}\)

完结撒花

嘿嘿嘿,如果你算了一下复杂度的话,会发现递推的复杂度是 \(O(n^2)\) ,显然会T。

上面那个东西,和卡特兰数有很大的关系。其实就是求卡特兰数 \(f_n\)

所以我们要说关于卡特兰数的几个不一样的公式:

  1. \(f_n=\sum\limits_{i=0}^{n-1}f_i\cdot f_{n-i-1}\) 这个也是递推的,复杂度依旧不对。
  2. \(f_{n}=f_{n-1}\cdot \frac {4n-2}{n+1}\) ,如果 \(p\) 是质数可以直接使用,也就是这个题。但是这个题没有保证,是做不了了的。
  3. \(f_n=\frac {C_{2n}^n}{n+1}\) ,这个是可以的。

为什么这个就是可以的?因为 \(f_n=\frac {C_{2n}^n}{n+1}=\frac {2n\times (2n-1)\times \cdots\times (n+2)}{n!}\) ,而一个数又可以表示成 \(\prod p_i^{k_i}\) ,所以我们可以将每个数做质因数分解,统计每个质因子的指数即可。

代码

#include<bits/stdc++.h>
#define ll long long

using namespace std;
const int N=2e6+10;
int min_p[N],p[N],cnt[N],tot,mod,n;

inline int fpow(int a,int b){
    int res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

inline void init(int n){
    for(int i=2;i<=n;i++){
        if(!min_p[i]) p[++tot]=i,min_p[i]=i;
        for(int j=1;j<=tot&&p[j]*i<=n;j++){
            min_p[i*p[j]]=p[j];
            if(i%p[j]==0) break;
        }
    }
}

int main(){
    scanf("%d%d",&n,&mod);
    init(2*n);
    for(int i=1;i<=n;i++) cnt[i]=-1;
    for(int i=n+2;i<=2*n;i++) cnt[i]=1;
    for(int i=2*n;i>1;i--)
        if(min_p[i]<i) cnt[min_p[i]]+=cnt[i],cnt[i/min_p[i]]+=cnt[i];
    int ans=1;
    for(int i=2;i<=2*n;i++)
        if(min_p[i]==i) ans=(ll)ans*fpow(i,cnt[i])%mod;
    printf("%lld\n",ans);
}
posted @ 2020-10-19 21:32  jasony_sam  阅读(127)  评论(0编辑  收藏  举报