[HNOI2009]有趣的数列

[HNOI2009]有趣的数列

Description

我们称一个长度为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的值。

Input

输入文件只包含用空格隔开的两个整数n和P。输入数据保证,50%的数据满足n≤1000,100%的数据满足n≤1000000且P≤1000000000。

Output

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

Sample Input

3 10

Sample Output

5
对应的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)。

题解

法一:

找规律发现前几项为卡特兰数列,再根据快速计算组合数的方法进行计算

f ( n ) =C(n, 2n)/(n+1) = (2n)!/(n!*(n+1)!)

AC代码:

#include <bits/stdc++.h>//[HNOI2009]有趣的数列
#define LL long long
using namespace std;
const LL MAXN = 1000005;
LL n, p, cnt;
bool v[MAXN];
LL primes[2 * MAXN];
bool isHeshu[2 * MAXN];
LL oula(LL x);        //质数筛
LL qkpow(LL x, LL y); //快速幂
int main()
{

    scanf("%d%d", &n, &p);
    oula(2 * n);
    LL ans = 1;
    for (LL i = 1; i <= cnt; ++i)
    {
        LL cnt1 = 0, cnt2 = 0, cnt3 = 0;
        LL pm = primes[i];
        while (pm <= 2 * n)
        {
            cnt1 += n / pm;
            cnt2 += (n + 1) / pm;
            cnt3 += 2 * n / pm;
            pm *= primes[i]; //当前质因子次数+1
        }
        LL sl = cnt3 - cnt1 - cnt2;
        ans *= qkpow(primes[i], sl);
        ans %= p;
    }
    printf("%lld\n", ans);
    return 0;
}
LL oula(LL x)
{
    for (LL i = 2; i <= x; ++i)
    {
        if (!isHeshu[i])
        {
            primes[++cnt] = i;
        }
        for (LL j = 1; primes[j] * i <= x; ++j)
        {
            isHeshu[primes[j] * i] = 1;
            if (i % primes[j] == 0)
                break;
        }
    }
}

LL qkpow(LL x, LL y)
{
    LL sum = x;
    LL ret = 1;
    while (y)
    {
        if (y & 1)
        {
            ret = ret * sum % p;
        }
        sum = sum * sum % p;
        y >>= 1;
    }
    return ret;
}

快速计算组合数的方法:

对于本题中f ( n ) =C(n, 2n)/(n+1) = (2n)!/(n!*(n+1)!) ,可以枚举1~2n之间的素数然后计算这些素数分别出现的次数 , 消掉部分 , 再将剩余的利用快速幂算法乘进答案中

for (LL i = 1; i <= cnt; ++i)
    {
        LL cnt1 = 0, cnt2 = 0, cnt3 = 0;
        LL pm = primes[i];
        while (pm <= 2 * n)
        {
            //找相应的素数出现的次数
            cnt1 += n / pm;
            cnt2 += (n + 1) / pm;
            cnt3 += 2 * n / pm;
            pm *= primes[i]; //当前质因子次数+1
        }
        LL sl = cnt3 - cnt1 - cnt2;
        ans *= qkpow(primes[i], sl);
        ans %= p;
    }


LL qkpow(LL x, LL y)//快速幂
{
    LL sum = x;
    LL ret = 1;
    while (y)
    {
        if (y & 1)
        {
            ret = ret * sum % p;
        }
        sum = sum * sum % p;
        y >>= 1;
    }
    return ret;
}

法二DP:

如果确定了奇数项,那么数列要么不合法,要么唯一确定 , 继续观察后发现奇数项只要满足ai<2i即可

, 故可以得到一个n^2的dp

#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define ll long long
#define pa pair<int,int>
#define mod 1000000007
#define inf 100000000
using namespace std;
int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x;
}
int n,P;
int f[1005][2005],s[1005][2005];//f[i][j]表示前i位填到数字j的方案,即第i位用的是j,用前缀和加速转移
int main()
{
	n=read();P=read();
	f[0][0]=1;
	for(int i=0;i<=2*n;i++)s[0][i]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=2*i-1;j++)
		{
			f[i][j]=s[i-1][j-1]%P;
		}
		for(int j=1;j<=2*n;j++)
			s[i][j]=(s[i][j-1]+f[i][j])%P;
	}
	printf("%d",s[n][2*n]);
	return 0;
}
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值