有趣的数列

题目连接:

https://www.acwing.com/problem/content/1318/

Description

我们称一个长度为 2n 的数列是有趣的,当且仅当该数列满足以下三个条件:
1.它是从 1 到 2n 共 2n 个整数的一个排列 ({ai})
2.所有的奇数项满足 (a_1<a_3<⋯<a_{2n−1}) ,所有的偶数项满足 (a_2<a_4<⋯<a_{2n})
3.任意相邻的两项 (a_{2i−1})(a_{2i}(1≤i≤n)) 满足奇数项小于偶数项,即:(a_{2i−1}<a_{2i})
任务是:对于给定的 n,请求出有多少个不同的长度为 2n 的有趣的数列。
因为最后的答案可能很大,所以只要求输出答案 modP 的值。

Input

只包含用空格隔开的两个整数 n 和 P。
(1≤n≤10^6,)
(2≤P≤10^9)

Output

仅含一个整数,表示不同的长度为 2n 的有趣的数列个数 modP 的值。

Sample Input

3 10

Sample Output

5

Hint

对应的5个有趣的数列分别为 :
{1,2,3,4,5,6} , {1,2,3,5,4,6} , {1,3,2,4,5,6} , {1,3,2,5,4,6} , {1,4,2,5,3,6}。

题解:

我们构造出一个奇数项和一个偶数项序列序列,最后两个序列分别排序,放在对应位置,可以构造出的数量就是答案。我们从1~2n开始依次选,任何时候奇数项选的数量一定要大于等于偶数项选的数量。所以直接求卡特兰数即刻C(2n,n)-C(2n,n-1)。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2000010;
typedef long long LL;
int st[N],primes[N],tot,n,p,sum[N];
void get_primes(){
    for(int i=2;i<N;++i){
        if(!st[i]) primes[++tot]=i;
        for(int j=1;primes[j]*i<N;++j){
            st[primes[j]*i]=1;
            if(i%primes[j]==0) break;
        }
    }
}
int get(int a,int b){
    int s=0;
    while(a) s+=a/b,a/=b;
    return s;
}
LL qmi(int a,int b){
    int res=1;
    while(b) {
        if(b&1) res=(LL)res*a%p;
        a=(LL)a*a%p;
        b>>=1;
    }
    return res;
}
LL C(int a,int b){
    for(int i=1;i<=tot;++i){
        int prime=primes[i];
        sum[i]=get(a,prime)-get(b,prime)-get(a-b,prime);
    }
    LL res=1;
    for(int i=1;i<=tot;++i){
        res=(res*qmi(primes[i],sum[i]))%p;
    }
    return res;
}
int main(){
    cin>>n>>p;
    get_primes();
    cout<<(C(2*n,n)-C(2*n,n-1)+p)%p<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/jjl0229/p/12725070.html