1. 程式人生 > >[HNOI 2009] 有趣的數列

[HNOI 2009] 有趣的數列

[題目連結]

         https://www.lydsy.com/JudgeOnline/problem.php?id=1485

[演算法]

        我們不妨從1-2N依次選取奇數項

        考慮到P2n-1 < P2n , 顯然 , 當1至i中 , 不選的數 > 選了的數 , 不合法

        那麼 , 當一個序列滿足 : 1-i中 , 不選的數 > 選了的數 , 則這個序列是“有趣”的

        可以把該問題轉化為一個經典模型 : 滿足前i位0的個數 <= 1的個數的長度為2N的二進位制數有多少個 , 答案為卡特蘭數的第n項

        卡特蘭數的通項公式 : Cn = C(2n , n) - C(2n , n - 1)

        由於答案對P取模 , 而P不是質數 , 逆元可能不存在 , 我們需要在求組合數時求出每個質因子在階乘中出現了多少次 , 然後快速冪計算答案即可

        時間複雜度 : O(NlogN)

[程式碼]

         

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000010
#define MAXP 2000010

int n , P , tot;
int f[MAXP] , prime[MAXP] , a[MAXP] , b[MAXP] , loc[MAXP];

template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); }
template <typename T> inline void
chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline int exp_mod(int a , int n) { int res = 1 , b = a; while (n > 0) { if (n & 1) res = 1LL * res * b % P; b = 1LL * b * b % P; n >>= 1; } return res; } inline int C(int x , int y) { for (int i = 1; i <= tot; i++) a[i] = 0; for (int k = 1; k <= x; k++) { int tmp = k; for (int i = 1; i <= tot && 1LL * prime[i] * prime[i] <= k; i++) { if (tmp % prime[i] == 0) { while (tmp % prime[i] == 0) { tmp /= prime[i]; ++a[i]; } } if (tmp == 1 || loc[tmp]) break; } if (tmp > 1) { int pos = loc[tmp]; ++a[pos]; } } for (int k = 1; k <= y; k++) { int tmp = k; for (int i = 1; i <= tot && 1LL * prime[i] * prime[i] <= k; i++) { if (tmp % prime[i] == 0) { while (tmp % prime[i] == 0) { tmp /= prime[i]; --a[i]; } } if (tmp == 1 || loc[tmp]) break; } if (tmp > 1) { int pos = loc[tmp]; --a[pos]; } } for (int k = 1; k <= x - y; k++) { int tmp = k; for (int i = 1; i <= tot && 1LL * prime[i] * prime[i] <= k; i++) { if (tmp % prime[i] == 0) { while (tmp % prime[i] == 0) { tmp /= prime[i]; --a[i]; } } if (tmp == 1 || loc[tmp]) break; } if (tmp > 1) { int pos = loc[tmp]; --a[pos]; } } int ans = 1; for (int i = 1; i <= tot; i++) ans = 1LL * ans * exp_mod(prime[i] , a[i]) % P; return ans; } int main() { read(n); read(P); for (int i = 2; i < MAXP; i++) { if (!f[i]) { f[i] = i; prime[++tot] = i; loc[i] = tot; } for (int j = 1; j <= tot; j++) { int tmp = i * prime[j]; if (tmp >= MAXP) break; f[tmp] = prime[j]; if (prime[j] == f[i]) break; } } printf("%d\n" , (C(2 * n , n) - C(2 * n , n - 1) + P) % P); return 0; }