洛谷-3200 [HNOI2009]有趣的数列

本文探讨了一个数学问题,即寻找满足特定条件的有趣数列的数量,并将其转化为卡特兰数的经典问题。通过使用高效的算法,文章详细介绍了如何计算这类数列的数量,即使在大数情况下也能得到准确结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述
我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件:
(1)它是从1到2n共2n个整数的一个排列{ai};
(2)所有的奇数项满足a1<a3<…<a2n-1,所有的偶数项满足a2<a4<…<a2n;
(3)任意相邻的两项a2i-1与a2i(1<=i<=n)满足奇数项小于偶数项,即:a2i-1<a2i。
现在的任务是:对于给定的n,请求出有多少个不同的长度为2n的有趣的数列。因为最后的答案可能很大,所以只要求输出答案 mod P的值。
输入格式
输入文件只包含用空格隔开的两个整数n和P。输入数据保证,50%的数据满足n<=1000,100%的数据满足n<=1000000且P<=1000000000。
输出格式
仅含一个整数,表示不同的长度为2n的有趣的数列个数mod P的值。

输入输出样例
输入 #1
3 10

输出 #1
5

解释:可以转换成括号匹配问题,奇数项我们看成右括号,偶数项看成左括号,每项的数x代表一个字符串中第X项放什么括号。变成很经典的问题了。直接上卡特兰数
ans=C2∗nnn+1ans=\frac{C_{2*n}^n}{n+1}ans=n+1C2nn,因为p不一定是质数,所以通过统计质因子来计算就好了

#include<bits/stdc++.h>
using namespace std;
const int N=2000005;
int mp[N],p[N/10],cnt[N],mod;
int pow(int a,int b){
    int ans=1;
    while(b){
        if(b&1) ans=(long long)ans*a%mod;
        a=(long long)a*a%mod;b>>=1;
    }
    return ans;
}
int main(){
    int n;
    cin>>n>>mod;
    int pn=0;
    for(int i=2;i<=2*n;i++){
        if(!mp[i]){
            p[++pn]=i;
            mp[i]=i;
        }
        for(int j=1;j<=pn&&i*p[j]<=2*n;j++){
            mp[i*p[j]]=p[j];
            if(i%p[j]==0) break;
        }
    }
    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(mp[i]<i){
            cnt[mp[i]]+=cnt[i];
            cnt[i/mp[i]]+=cnt[i];
        }
    int ans=1;
    for(int i=2;i<=2*n;i++)
        if(mp[i]==i)
            ans=(long long)ans*pow(i,cnt[i])%mod;
    cout<<ans<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值