题目-有趣的数列

问题分析
尝试将偶数项作为横坐标, 奇数项作为纵坐标
因为要求 a 2 i − 1 < a 2 i a_{2i - 1} < a_{2i} a2i−1<a2i, 也就是说选法一定在 y = x y = x y=x直线下方

相当于终点是 ( n , n ) (n, n) (n,n), 求合法的路径的数量, 就是 C a t a l a n Catalan Catalan序列
算法步骤
实现 C 2 n n − C 2 n n − 1 C_{2n} ^ n - C_{2n} ^ {n - 1} C2nn−C2nn−1对 P P P取模的结果即可
因为 P ≤ 1 0 9 P \le 10 ^ 9 P≤109不能用 l u c a s lucas lucas定理, 但是发现 n ≤ 1 0 6 n \le 10 ^ 6 n≤106, 尝试用快速幂求逆元的方式计算组合数, 但是发现 P P P不一定是质数, 不能用费马小定理求解
求解该方程
x ⋅ t ≡ 1 ( m o d P ) x \cdot t \equiv 1(\mod P) x⋅t≡1(modP)
尝试用扩展欧几里得算法计算, 但是前提是 x , P x, P x,P互质, 才能用 e x g c d exgcd exgcd算法求解, 还是不行
代码实现
忽略了 x , P x, P x,P互质情况的错误代码
快速幂求逆元的前提是 P P P是质数, 扩展欧几里得算法求逆元的前提条件是 x , P x, P x,P互质!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 10;
int n, P;
LL fact[N], infact[N];
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL _x, _y;
LL d = exgcd(b, a % b, _x, _y);
x = _y, y = x - (a / b) * _y;
return d;
}
void init(LL n) {
fact[0] = infact[0] = 1;
for (int i = 1; i <= n; ++i) {
fact[i] = fact[i - 1] % P * i % P;
LL x, y;
exgcd(fact[i] % P, P, x, y);
infact[i] = x;
}
}
LL C(int a, int b) {
if (b < 0 || a < b) return 0;
LL ans = fact[a] % P * infact[b] % P * infact[a - b] % P;
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> P;
init(2 * n);
LL ans = C(2 * n, n) - C(2 * n, n - 1);
ans = (ans % P + P) % P;
cout << ans << '\n';
return 0;
}
阶乘分解法求组合数, 注意每次计算组合数的时候清空 m a p map map
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 10;
int n, P;
int primes[N], cnt;
bool st[N];
map<int, int> mp;
void init(int n) {
for (int i = 2; i <= n; ++i) {
if (!st[i]) primes[cnt++] = i;
for (int j = 0; primes[j] <= n / i; ++j) {
st[i * primes[j]] = true;
if (i % primes[j] == 0) break;
}
}
}
void div(LL a, bool flag) {
for (int i = 0; i < cnt; ++i) {
int p = primes[i], t = 0;
for (LL j = a; j; j /= p) t += j / p;
if (t == 0) continue;
flag ? mp[p] += t : mp[p] -= t;
}
}
LL C(LL a, LL b) {
mp.clear();
if (b < 0 || a < b) return false;
div(a, true), div(b, false), div(a - b, false);
LL ans = 1;
for (int i = 0; i < cnt; ++i) {
int p = primes[i];
for (int i = 0; i < mp[p]; ++i) ans = ans * p % P;
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
init(N - 1);
cin >> n >> P;
LL ans = C(2 * n, n) - C(2 * n, n - 1);
ans = (ans % P + P) % P;
cout << ans << '\n';
return 0;
}
快速幂优化阶乘分解代码, 注意每次乘法都要进行取模操作
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e6 + 10;
int n, P;
int primes[N], cnt;
bool st[N];
map<int, int> mp;
void init(int n) {
for (int i = 2; i <= n; ++i) {
if (!st[i]) primes[cnt++] = i;
for (int j = 0; primes[j] <= n / i; ++j) {
st[i * primes[j]] = true;
if (i % primes[j] == 0) break;
}
}
}
void div(LL a, bool flag) {
for (int i = 0; i < cnt; ++i) {
int p = primes[i], t = 0;
for (LL j = a; j; j /= p) t += j / p;
if (t == 0) continue;
flag ? mp[p] += t : mp[p] -= t;
}
}
LL q_pow(LL a, LL b, int mod) {
LL ans = 1;
a %= mod;
while (b) {
if (b & 1) ans = ans % mod * a % mod;
a = a % mod * a % mod;
b >>= 1;
}
return ans % mod;
}
LL C(LL a, LL b) {
mp.clear();
if (b < 0 || a < b) return false;
div(a, true), div(b, false), div(a - b, false);
LL ans = 1;
for (int i = 0; i < cnt; ++i) {
int p = primes[i];
ans = ans % P * q_pow(p, (LL) mp[p], P);
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
init(N - 1);
cin >> n >> P;
LL ans = C(2 * n, n) - C(2 * n, n - 1);
ans = (ans % P + P) % P;
cout << ans << '\n';
return 0;
}

京公网安备 11010502036488号