zoukankan      html  css  js  c++  java
  • 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 imes a) ,但是 (2 imes 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=sumlimits_{i=0}^{n-1}f_icdot 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 imes (2n-1) imes cdots imes (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
    ",ans);
    }
    
  • 相关阅读:
    Java并发教程(Oracle官方资料)
    java中的字符集和编码
    一篇文章看懂Java并发和线程安全
    mysql中,如何查看数据库中当前可用的校勘?字符集默认的collation?
    mysql数据库中,查看当前支持的字符集有哪些?字符集默认的collation的名字?
    mysql数据库中,通过一条insert into语句,同时插入多个值
    jenkins第一次登陆,输入完密码之后,卡在了SetupWizard[jenkins]处
    mysql数据库,如何在登录mysql之后执行操作系统上的SQL脚本?
    mysql在命令行中,指定要连接的数据库?
    mysql中如何在命令行中,执行一个SQL脚本文件?
  • 原文地址:https://www.cnblogs.com/jasony/p/13843259.html
Copyright © 2011-2022 走看看