[HNOI 2009] 有趣的數列
阿新 • • 發佈:2018-10-31
[題目連結]
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 voidchkmin(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; }