信息学奥赛一本通 1661:有趣的数列 | 洛谷 P3200 [HNOI2009] 有趣的数列

【题目链接】

ybt 1661:有趣的数列
洛谷 P3200 [HNOI2009] 有趣的数列

【题目考点】

1. 组合数学:卡特兰数

【解题思路】

已知要得到1到 2 n 2n 2n的一个排列,第 i i i位置的数为 a i a_i ai。

下标为奇数位置的元素为奇数项 ,下标为偶数位置的元素为偶数项

本题所有的奇数项是升序的,所有的偶数项是升序的,任意相邻的两项 a 2 i − 1 a_{2i-1} a2i−1 与 a 2 i a_{2i} a2i 满足: a 2 i − 1 < a 2 i a_{2i-1}<a_{2i} a2i−1<a2i。

对于在第 2 i 2i 2i位置的数 a 2 i a_{2i} a2i,该数一定大于前面的偶数项, a 2 i a_{2i} a2i也大于大于 a 2 i − 1 a_{2i-1} a2i−1, a 2 i − 1 a_{2i-1} a2i−1大于前面所有奇数项。

所以偶数项 a 2 i a_{2i} a2i一定大于所有其前面位置的数。

而下标小于 2 i 2i 2i的数有 2 i − 1 2i-1 2i−1个,这 2 i − 1 2i-1 2i−1个数取最小的数时, a 2 i a_{2i} a2i的值为 2 i 2i 2i。其他情况下, a 2 i a_{2i} a2i的值要大于 2 i 2i 2i。因此偶数项的数值一定大于等于其下标

如果将得到该序列的过程理解为将整数 1 ∼ 2 n 1\sim 2n 1∼2n依次填到 1 ∼ 2 n 1\sim 2n 1∼2n的位置上。所有的位置可以分为:偶数位置、奇数位置。

那么当前的数只能填在当前还空着的最小的偶数位置或奇数位置。

设当前空着的最小的偶数位置为 2 i 2i 2i,如果将当前的数 x x x放到比 2 i 2i 2i更大的偶数位置 2 j 2j 2j上,那么接下来填的数都大于 x x x,最终会有一个大于 x x x的数 y y y填在了 2 i 2i 2i位置。此时 2 i < 2 j 2i<2j 2i<2j同时 a 2 i = y , a 2 j = x , a 2 i > a 2 j a_{2i}=y, a_{2j}=x, a_{2i}>a_{2j} a2i=y,a2j=x,a2i>a2j,而本题要求对于偶数位置,当 i < j i<j i<j时 a 2 i < a 2 j a_{2i}<a_{2j} a2i<a2j,产生矛盾。

对于奇数位置亦是同理。

那么已填的偶数位置数量一定小于等于已填的奇数位置的数量。

反证法:如果已经填了a个偶数位置,b被奇数位置,且 a > b a>b a>b。

那么第a个偶数位置的下标为 2 a 2a 2a,设该位置填的值为 x x x,那么根据偶数项的数值一定大于等于其下标,有 x > 2 a x>2a x>2a。

而此时一共填了 a + b a+b a+b个数,所有填下的数的最大值为 a + b a+b a+b,所以 x < a + b x<a+b x<a+b。

由于 a > b a>b a>b,所以 x < a + b < 2 a x<a+b<2a x<a+b<2a,与 x > 2 a x>2a x>2a产生矛盾。

因此原命题得证。

可以将在偶数位置填数当做操作 a a a,在奇数位置填数当做操作 b b b,一共有 2 n 2n 2n次操作,其中操作 a a a的次数要始终小于等于操作 b b b的次数,求操作的方案数,为卡特兰数。

求卡特兰数可以使用公式: C a t a l a n n = C 2 n n n + 1 Catalan_n=\frac{C_{2n}^n}{n+1} Catalann=n+1C2nn

即: C a t a l a n n = ( 2 n ) ! n ! ( n + 1 ) ! = 2 n ( 2 n − 1 ) . . . ( n + 2 ) n ( n − 1 ) . . .1 Catalan_n=\frac{(2n)!}{n!(n+1)!}=\frac{2n(2n-1)...(n+2)}{n(n-1)...1} Catalann=n!(n+1)!(2n)!=n(n−1)...12n(2n−1)...(n+2)

本题结果要对 P P P取模,而 P P P可能不是质数,因此不能对分母求逆元。

可以将分子 ( 2 n ) ! (2n)! (2n)!与分母 n ! ( n + 1 ) ! n!(n+1)! n!(n+1)!分解质因数。

具体做法,可以先写线性筛,得到 1 ∼ n 1\sim n 1∼n每个数的最小质因数,而后通过对每个数不断除以其最小质因数,直到该数为1,进而得到该数的所有质因数。

设一个映射mpmp[i]表示结果中质因数 i i i的指数。

统计分子 ( 2 n ) ! (2n)! (2n)!的所有质因数出现的次数,质因数 p p p每出现一次,就让 p p p的指数增加1。

统计分母 n ! ( n + 1 ) ! n!(n+1)! n!(n+1)!的所有质因数出现的次数,质因数 p p p每出现一次,就让 p p p的指数减少1。

最终将mp中保存的各质因数的底数和指数求快速幂,而再乘积,注意对 P P P取模,即可得到本题的结果。

整体时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

【题解代码】

解法1:卡特兰数

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define N 2000005
typedef long long LL; 
bool isPrime[N];
int prime[N], pn, minPrime[N];
map<int, int> mp;
void linearSeive(int n)
{
	memset(isPrime, 1, sizeof(isPrime));
	for(int i = 2; i <= n; ++i)
	{
		if(isPrime[i])
		{
			prime[++pn] = i;
			minPrime[i] = i;
		}
		for(int j = 1; j <= pn && i*prime[j] <= n; ++j)
		{
			isPrime[i*prime[j]] = false;
			minPrime[i*prime[j]] = prime[j];
			if(i%prime[j] == 0)
				break; 
		}
	}
}
LL fastPow(LL a, LL b, LL m)
{
	LL r = 1; 
	while(b > 0)
	{
		if(b & 1)
			r = r*a%m;
		a = a*a%m;
		b >>= 1;
	}
	return r;
}
int main()
{
	LL n, p, ans = 1;
	cin >> n >> p;
	linearSeive(2*n);
	for(int i = n+2; i <= 2*n; ++i)
		for(int j = i; j > 1; j /= minPrime[j])
			mp[minPrime[j]]++;
	for(int i = 2; i <= n; ++i)
		for(int j = i; j > 1; j /= minPrime[j])
			mp[minPrime[j]]--;
	for(pair<int, int> pa : mp)
		ans = ans*fastPow(pa.first, pa.second, p)%p;
	cout << ans;
    return 0;
}
相关推荐
程序员:钧念2 小时前
深度学习与强化学习的区别
人工智能·python·深度学习·算法·transformer·rag
英英_3 小时前
MATLAB数值计算基础教程
数据结构·算法·matlab
一起养小猫3 小时前
LeetCode100天Day14-轮转数组与买卖股票最佳时机
算法·leetcode·职场和发展
hele_two3 小时前
快速幂算法
c++·python·算法
OopspoO4 小时前
C++杂记——Name Mangling
c++
yuanmenghao4 小时前
车载Linux 系统问题定位方法论与实战系列 - 车载 Linux 平台问题定位规范
linux·运维·服务器·网络·c++
小羊羊Python4 小时前
SoundMaze v1.0.1正式发布!
开发语言·c++
l1t4 小时前
利用DeepSeek将python DLX求解数独程序格式化并改成3.x版本
开发语言·python·算法·数独
jllllyuz4 小时前
基于子集模拟的系统与静态可靠性分析及Matlab优化算法实现
算法·matlab·概率论