【题目链接】
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,进而得到该数的所有质因数。
设一个映射mp,mp[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;
}