Luogu P3200 [HNOI2009]有趣的数列

题意

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

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

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

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

( exttt{Data Range:}1leq nleq 10^6,1leq mleq 10^9)

题解

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

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

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

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

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

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

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

首先可以枚举一个质数 (p),然后枚举他的倍数 (q)。接下来不断用 (q) 除掉 (p),然后顺便对答案产生贡献。容易看出每个数的每个质因子只被考虑到一次,所以复杂度是 (O(nlog 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
",res);
}
原文地址:https://www.cnblogs.com/Karry5307/p/13489385.html