Luogu P3200 [HNOI2009]有趣的数列

题意

给定 \(n\),求有多少个长度为 \(2n\) 的排列 \(p\) 满足

  • 对于 \(1\leq i\leq n\)\(p_{2i-1}<p_{2i}\)

  • \(p_1<p_3<\cdots<p_{2n-1},p_2<p_4<\cdots<p_{2n}\)

答案对给定的模数 \(m\) 取模,不保证 \(m\) 为质数

\(\texttt{Data Range:}1\leq n\leq 10^6,1\leq m\leq 10^9\)

题解

注意到我们可以奇偶分组,最后合并一下。

接下来考虑将因为一个小于号是 \(2\) 个元素,另一个是 \(n\) 个元素,所以考虑将排列与合法的入栈出栈过程建立映射。

如果某一个元素入栈了,那么往奇数部分填上这个元素的入栈时间,出栈的话则往偶数部分填。

由于入栈时间和出栈时间有序,而且弹掉 \(n\) 个元素的时间总比将 \(n\) 个元素入栈的时间晚,所以可以满足所有的限制。

注意到不同过程的总数就是卡塔兰数,所以答案就出来了。

但是由于这题需要组合数模合数,所以要对每个数做唯一分解,但是这样是 \(O(n\sqrt{n})\) 的。

注意到 \(1\sim n\) 中每个质因子对答案的贡献为 \(1\)\(n+1\)\(0\),而 \(n+2\sim 2n\)\(-1\),所以我们需要求出 \(1\sim 2n\) 的所有质因子,这个过程可以仿照埃氏筛来做。

首先可以枚举一个质数 \(p\),然后枚举他的倍数 \(q\)。接下来不断用 \(q\) 除掉 \(p\),然后顺便对答案产生贡献。容易看出每个数的每个质因子只被考虑到一次,所以复杂度是 \(O(n\log n)\) 的,可以通过。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=1e6+51;
ll n,MOD,res=1,ptot,tmp,sgn;
ll np[MAXN<<1],fct[MAXN<<1];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
int main()
{
    n=read(),MOD=read();
    for(register int i=2;i<=2*n;i++)
    {
        if(!np[i])
        {
            for(register int j=1;i*j<=2*n;j++)
            {
                np[i*j]=1,tmp=i*j,sgn=i*j<=n?-1:i*j==n+1?0:1;
                while(tmp%i==0)
                {
                    fct[i]+=sgn,tmp/=i;
                }
            }
        }
    }
    for(register int i=2;i<=2*n;i++)
    {
        while(fct[i])
        {
            res=(li)res*i%MOD,fct[i]--;
        }
    }
    printf("%d\n",res);
}
posted @ 2020-08-12 10:58  Karry5307  阅读(108)  评论(0编辑  收藏  举报