• luogu P3200 [HNOI2009]有趣的数列|Catalan数|数论


    luogu P3200 [HNOI2009]有趣的数列|Catalan数|数论

    Problem

    Problem

    分析

    分析题目可得:

    答案取模前就是Catalan数:

    [ans(取模前)=frac{C_{n}^{2n}}{left(n+1 ight)}=frac{2n!}{n!(n+1)!} ]

    根据n的范围:[0,1e6]可知超long long

    所以题目给出p来膜

    众所周知,带有除法的式子是不能直接每一步进行求余的

    这时候可能会想到逆元。

    众所周知,逆元中膜的数是质数

    所以逆元也不行。

    众所周知,ans(取模前)一定是整数,观察ans表达式:

    如果对2n!进行整数唯一分解,即:

    [2n!=a_1^{b_1}cdot a_2^{b_2}cdot a_3^{b_3}cdot...cdot a_i^{b_i} ]

    同理对n!和(n+1)!进行整数唯一分解

    如果2n!和n!与(n+1)!有相同分解ai,则2n!中次数一定大于n!和(n+1)!的次数(因为ans是整数)

    所以ans可以进一步化解:

    [ans=a_1^{b_1-b_1'-b_1''}cdot a_2^{b_2-b_2'-b_2''}cdot a_3^{b_3-b_3'-b_3''}cdot a_i^{b_i-b_i'-b_i''} ]

    于是问题变成对阶乘进行整数唯一分解

    对阶乘n!进行整数唯一分解的方法:

    [exists 质数m_iin[2,n!],cnt[m_i]=n/m_i+n/m_i^2+n/m_i^3+...+n/m_i^k,m^kleqslant n ]

    综上所述,程序分为三个部分:

    1、欧拉筛求质数

    2、快速幂

    3、进行catalan计算

    Code

    1、欧拉筛求质数

    void Euler(ll n){
    	memset(isprime,1,sizeof(isprime));
    	isprime[0]=isprime[1]=0;
    	for(ll i=2;i<=n;i++){
    		if(isprime[i]) m[++cnt]=i;
    		for(ll j=1;i*m[j]<=n;j++){
    			isprime[i*m[j]]=0;
    			if(i%m[j]==0) break;
    		}
    	}
    }
    

    2、快速幂

    ll qpow(ll a,ll b){
    	ll ans=1,base=a;
    	while(b){
    		if(b&1) ans=ans*base%p;
    		base=base*base%p;
    		b>>=1;
    	}
    	return ans%p;
    }
    

    3、求Catalan数

    ll catalan(ll n){
    	ll ans=1;
    	for(ll i=1;i<=cnt&&m[i]<=2*n;i++){
    		ll cnt1=0,cnt2=0,cnt3=0;
    		ll pm=m[i];
    		while(pm<=2*n){
    			cnt1+=2*n/pm,cnt2+=n/pm,cnt3+=(n+1)/pm;
    			pm*=m[i];
    		}
    		ans=ans*qpow(m[i],cnt1-cnt2-cnt3)%p;
    	}
    	return ans;
    }
    

    代码:

    /*
    C2n-n/(n+1)
    =2n!/n!(n+1)
    */
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #define ll long long 
    using namespace std;
    const int maxn=2e6+5;
    ll n,p;
    ll isprime[maxn],m[maxn],cnt=0;
    ll qpow(ll a,ll b){
    	ll ans=1,base=a;
    	while(b){
    		if(b&1) ans=ans*base%p;
    		base=base*base%p;
    		b>>=1;
    	}
    	return ans%p;
    }
    void Euler(ll n){
    	memset(isprime,1,sizeof(isprime));
    	isprime[0]=isprime[1]=0;
    	for(ll i=2;i<=n;i++){
    		if(isprime[i]) m[++cnt]=i;
    		for(ll j=1;i*m[j]<=n;j++){
    			isprime[i*m[j]]=0;
    			if(i%m[j]==0) break;
    		}
    	}
    }
    ll catalan(ll n){
    	ll ans=1;
    	for(ll i=1;i<=cnt&&m[i]<=2*n;i++){
    		ll cnt1=0,cnt2=0,cnt3=0;
    		ll pm=m[i];
    		while(pm<=2*n){
    			cnt1+=2*n/pm,cnt2+=n/pm,cnt3+=(n+1)/pm;
    			pm*=m[i];
    		}
    		ans=ans*qpow(m[i],cnt1-cnt2-cnt3)%p;
    	}
    	return ans;
    }
    int main(){
    	scanf("%lld%lld",&n,&p);
    	Euler(2*n);
    	printf("%lld",catalan(n));
    	return 0;
    }
    
  • 相关阅读:
    C#线程类Thread初步
    无限级分类存储过程版
    C#多线程编程实例实战
    数据库里阻塞和死锁情况 看那里死锁的存储过程
    预防按钮的多次点击 恶意刷新
    .net2.0文件压缩/解压缩
    HttpHandler和HttpModule入门
    反射,枚举,绑定下拉框
    在C#里关于定时器类
    判断上传的图片文件格式是否合法不是用后缀做的判断
  • 原文地址:https://www.cnblogs.com/saitoasuka/p/10383949.html
Copyright © 2020-2023  润新知