bzoj1485: [HNOI2009]有趣的数列(Catalan数)

时间:2023-01-15 10:09:50

  一眼卡特兰数...写完才发现不对劲,样例怎么输出$0$...原来模数不一定是质数= =...

  第一次见到模数不是质数的求组合数方法$(n,m\leq 10^7)$,记录一下...

  先对于$1$~$n$的每个数筛出最小的质因子(我肯定是写埃式筛啦),那么乘上一个数$x$相当于乘上$x$的所有质因子,所以从大到小扫一遍每一个数,若$x$被乘了$1$次且$x$不是质数,那么就给$x$的最小质因子$mn_x$和$\frac{x}{mn_x}$的次数+$1$,显然这样最后只会剩下质因子有记录次数,那么这个次数就是质因子的指数了。

  如果求$C(n,m)$,不妨设$n-m\leq m$,那么$m+1$~$n$被乘了$1$次,$1$~$n-m$被除了$1$次,也就是被乘了$-1$次,那么这些数的质因子都应该被减去相应次数。会不会有质因子被减到负数了?我们知道组合数一定是正整数,所以肯定不会出负数啦~

  最后扫一遍所有质数,快速幂一下就可以求得组合数了,$n$以内的质数个数是$O(\frac{n}{logn})$级别的,快速幂是$O(logn)$的,所以复杂度是$O(n)$的。

  对于这题使用卡特兰数的$C(2n,n)/(n+1)$的公式会比较好一些,不算$n+1$的贡献就好了

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=, inf=1e9;
int n, mod, ans;
int p[maxn], mn[maxn], cnt[maxn];
bool v[maxn];
inline void read(int &k)
{
int f=; k=; char c=getchar();
while(c<'' || c>'') c=='-'&&(f=-), c=getchar();
while(c<='' && c>='') k=k*+c-'', c=getchar();
k*=f;
}
inline void getp()
{
for(int i=;i<=n<<;i++)
if(!v[i])
{
p[++p[]]=i;
for(int j=i<<;j<=n<<;j+=i) v[j]=, !mn[j] && (mn[j]=i);
}
}
inline int power(int a, int b)
{
int ans=;
for(;b;b>>=, a=1ll*a*a%mod)
if(b&) ans=1ll*ans*a%mod;
return ans;
}
inline void add(int x, int delta)
{
cnt[x]+=delta;
if(v[x])
{
cnt[mn[x]]+=cnt[x];
cnt[x/mn[x]]+=cnt[x];
cnt[x]=;
}
}
int main()
{
read(n); read(mod); getp(); ans=;
for(int i=n<<;i>n+;i--) add(i, );
for(int i=n;i>;i--) add(i, -);
for(int i=;i<=p[];i++)
ans=1ll*ans*power(p[i], cnt[p[i]])%mod;
printf("%d\n", ans);
}