P3200 [HNOI2009]有趣的数列
首先发现它是个卡特兰数
-
硬算前几项的数字规律(我是这样做的)
-
可以转换为一个典型的卡特兰数的例子:
n个数排成两行,使右边都大于左边,后边都大于前边,求排法数量
将奇数看成第一行,将偶数看成第二行即可。
这道题的卡塔兰数,困难点在于模数不是质数,所以不能用费马小定理来求逆元,所以我们需要另辟蹊径
\(C_{2n}^n\)=\(\dfrac{2n!}{n!n!}\)
\(\dfrac{C_{2n}^{n}}{n+1}=\dfrac{2n!}{n!\left( n+1\right) !}\)
其实阶乘相比,有很多项其实是可以直接约掉的,根据质因数分解定理,就可以用下面的公式约掉
temp=0;
m=2*n;
while(m>0)
{
m=m/prime[i];
temp=temp+m;
}
m=n+1;
while(m>0)
{
m=m/prime[i];
temp=temp-m;
}
m=n;
while(m>0)
{
m=m/prime[i];
temp=temp-m;
}
ans=ans*quick_pow(prime[i],temp);//注意这里是乘不是加
然后,就没了
代码
#include<bits/stdc++.h>
using namespace std;
int n,mod,m;
int cnt;
const int N=2000000;
int prime[N];
bool vis[N];
int temp;
void divide(int x)
{
for(int i=2; i<=x; i++)
{
if(vis[i]==0)
{
prime[++cnt]=i;
}
for(int j=1; prime[j]<=x/i&&j<=cnt; j++)
{
vis[prime[j]*i]=1;
if((i%prime[j])==0) break;
}
}
}
long long quick_pow(int a,int b)
{
long long ans=1;
while(b)
{
if(b&1)ans=ans*a;
a=(long long)a*a%mod;
b>>=1;
}
return ans;
}
long long res=1;
int main()
{
cin>>n>>mod;
divide(n*2);
for(int i=1; i<=cnt; i++)
{
temp=0;
m=2*n;
while(m>0)
{
m=m/prime[i];
temp=temp+m;
}
m=n+1;
while(m>0)
{
m=m/prime[i];
temp=temp-m;
}
m=n;
while(m>0)
{
m=m/prime[i];
temp=temp-m;
}
res=((long long)res*quick_pow(prime[i],temp)%mod+mod)%mod;//注意这里是乘不是加
}
cout<<res<<endl;
return 0;
}