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

  一眼卡特兰数...写完才发现不对劲,样例怎么输出$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=2000010, inf=1e9;
int n, mod, ans;
int p[maxn], mn[maxn], cnt[maxn];
bool v[maxn];
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<'0' || c>'9') c=='-'&&(f=-1), c=getchar();
    while(c<='9' && c>='0') k=k*10+c-'0', c=getchar();
    k*=f;    
} 
inline void getp()
{
    for(int i=2;i<=n<<1;i++)
    if(!v[i])
    {
        p[++p[0]]=i;
        for(int j=i<<1;j<=n<<1;j+=i) v[j]=1, !mn[j] && (mn[j]=i);
    }
}
inline int power(int a, int b)
{
    int ans=1;
    for(;b;b>>=1, a=1ll*a*a%mod)
    if(b&1) 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]=0;
    }
}
int main()
{
    read(n); read(mod); getp(); ans=1;
    for(int i=n<<1;i>n+1;i--) add(i, 1);
    for(int i=n;i>1;i--) add(i, -1);
    for(int i=1;i<=p[0];i++)
    ans=1ll*ans*power(p[i], cnt[p[i]])%mod;
    printf("%d\n", ans);
}
View Code

 

posted @ 2018-02-09 23:28  Sakits  阅读(185)  评论(0编辑  收藏