BZOJ1485: [HNOI2009]有趣的数列(Catalan数,质因数分解求组合数)
题意
挺简洁的。
我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件:
(1)它是从1到2n共2n个整数的一个排列{ai};
(2)所有的奇数项满足a1<a3<…<a2n-1,所有的偶数项满足a2<a4<…<a2n;
(3)任意相邻的两项a2i-1与a2i(1≤i≤n)满足奇数项小于偶数项,即:a2i-1<a2i。
现在的任务是:对于给定的n,请求出有多少个不同的长度为2n的有趣的数列。因为最后的答案可能很大,所以只要求输出答案 mod P的值。
Sol
打表后发现时Catalan数。
通项公式:$\frac{C_{2n}^n}{n + 1}$
/* */ #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<vector> #include<set> #include<queue> #include<cmath> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/hash_policy.hpp> #define Pair pair<int, int> #define MP(x, y) make_pair(x, y) #define fi first #define se second //#define int long long #define LL long long #define rg register #define sc(x) scanf("%d", &x); #define pt(x) printf("%d ", x); #define db(x) double x #define rep(x) for(int i = 1; i <= x; i++) //#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++) //char buf[(1 << 22)], *p1 = buf, *p2 = buf; char obuf[1<<24], *O = obuf; #define OS *O++ = ' '; using namespace std; using namespace __gnu_pbds; const int MAXN = 1e5 + 10, INF = 1e9 + 10, mod = 1e9 + 7; const double eps = 1e-9; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int N; int a[MAXN], js[MAXN]; bool check() { for(int i = 1; i <= N - 2; i += 2) if(a[i] >= a[i + 2]) return 0; for(int i = 1; i <= N - 1; i += 2) if(a[i] >= a[i + 1]) return 0; for(int i = 2; i <= N - 2; i += 2) if(a[i] >= a[i + 2]) return 0; return 1; } main() { while(1) { N = read() << 1; js[0] = 1; for(int i = 1; i <= N; i++) js[i] = i * js[i - 1]; for(int i = 1; i <= N; i++) a[i] = i; int ans = 0; for(int i = 1; i <= js[N]; i++) { if(check()) ans++; next_permutation(a + 1, a + N + 1); } printf("%d\n", ans); } return 0; } /* 1 2 5 14 42 132 */
注意这里的模数不是质数,因此我们没法用逆元来求。
这里有一种最差$O(nlogn)$的算法
首先将每个数质因数分解,统计出每个质数的出现次数(除的话就是减去)
最后一起算即可
考虑到每个数的最小的质因数$ \geqslant 2$,因此极限复杂度为$O(n log n)$
/* */ #include<cstdio> //#define int long long #define LL long long const int MAXN = 2 * 1e6 + 10; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } int N, mod, prime[MAXN], vis[MAXN], tot, mn[MAXN], num[MAXN]; void GetPhi(int N) { vis[1] = 1; for(int i = 2; i <= N; i++) { if(!vis[i]) prime[++tot] = i, mn[i] = tot; for(int j = 1; j <= tot && (i * prime[j] <= N); j++) { vis[i * prime[j]] = 1; mn[i * prime[j]] = j; if(!(i % prime[j])) break; } } } void insert(int x, int opt) { while(x != 1) num[mn[x]] += opt, x = x / prime[mn[x]]; } int fastpow(int a, int p) { int base = 1; while(p) { if(p & 1) base = (1ll * base * a) % mod; a = (1ll * a * a) % mod; p >>= 1; } return base; } main() { N = read(); mod = read(); GetPhi(2 * N); for(int i = N + 1; i <= 2 * N; i++) insert(i, 1); for(int i = 1; i <= N; i++) insert(i, -1); insert(N + 1, -1); LL ans = 1; for(int i = 1; i <= tot; i++) if(num[i]) ans = (1ll * ans * fastpow(prime[i], num[i])) % mod; printf("%lld", ans); return 0; } /* 6 100 1 2 5 14 42 132 */
作者:自为风月马前卒
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。