首先%%%谢大佬 感谢她给我讲了这道题O(n)求组合数的方法
以及在这里贴上谢大佬这道题的题解,因为访问量太低谢大佬都伤心了->谢大佬的良心题解你值得拥有
1、我们可以得出这道题的答案是catalan数 详解见此
2、我们会发现这道题的p是不定的并且它可以不为素数。这时我们就可以引出这种神奇的求组合数方法了——
先用线性筛筛出每个数的质数以及最小质因数
接下来求cnt[x],cnt[x]意为因数x出现的次数。最终的结果是只有cnt[prime]有值,其他都没有值。我们再for一遍素数用快速幂就可以得出结果了。
从大到小for(m->1) ,根据该数是在分母还是分子来确定它是+1还是-1(也就是代码中的add(x,-1)还是add(x,1))
if(当i为素数时){cnt[i]++;}
if(当i不为素数时){
cnt[i]++;
cnt[d[i]]+=cnt[i],cnt[i/d[i]]+=cnt[i];
//把cnt[i]的值转移到cnt[d[i]]和cnt[i/d[i]]
cnt[i]=0;
//最后记得清空cnt[i]因为cnt[i]已经转移到cnt[它的因数]去了
}
但是i/d[i]有可能不是素数,没关系,从大到小for,之后又会for到i/d[i],又可以继续分解它了。
最后只有素数i的cnt[i]!=0
处理cnt[i] O(n)
for素数o(n/logn) * 快速幂O(logn)=O(n)
时间复杂度共O(n)
#include
#include
using namespace std;
const int N=1000000+5;
#define ll long long
int n,p;
bool noprime[2*N];int prime[N*2],d[2*N],size,cnt[2*N];
void euler()
{
d[1]=1;
for(int i=2;i<=2*n;i++){
if(!noprime[i]){
prime[++size]=i;
d[i]=i;
}
for(int j=1;j<=size&&i*prime[j]<=2*n;j++){
noprime[i*prime[j]]=1;
d[i*prime[j]]=prime[j];//!!!!
if(i%prime[j]==0) break;
}
}
}
void add(int x,int opt)
{
cnt[x]+=opt;
if(noprime[x]){
cnt[d[x]]+=cnt[x];
cnt[x/d[x]]+=cnt[x];
cnt[x]=0;
}
}
ll mpow(int a,int b)
{
ll base=a,ans=1;
for(int i=b;i;i>>=1,base=(base*base)%p)
if(i&1) ans=(ans*base)%p;
return ans;
}
int main()
{
scanf("%d%d",&n,&p);
euler();
for(int i=2*n;i>n+1/*catalan*/;i--) add(i,1);
for(int i=n;i>=1;i--) add(i,-1);//在上一波分解中如果分成了小于n的合数那么这个合数还需要被分
ll ans=1;
for(int i=1;prime[i]<=2*n&&i<=size;i++)
if(cnt[prime[i]])
ans=(ans*mpow(prime[i],cnt[prime[i]]))%p;
printf("%lld\n",ans);
return 0;
}