开始的时候了解过卡特兰数,但是不会做题,只会套公式……好吧……略过不提。
题意:给你n和m,叫你求卡特兰数h[n]%m的结果。
首先,公式h[n]=h[n-1]*(4*n-2)/(n+1)【注意这里n从0开始,是式子中的某一项,所以在应用实例中要记得进行区分(此n非彼n)】
由于数据很大,要取模,但是因为取模后分母就不一定整除分子了,所以不能对于结果直接取模,要分别进行。【其实所以结果可能是个小数?那怎么可以取模……】所以对分子取模的时候要求他的乘法逆元,但是乘法逆元必须是两个数要互质才可以,所以还要进行因数分解。【嗯,其实对于整个过程还不是太了解的说,但是大体能懂= =】。将分子和分母都进行分解,分解成与m互质和不与m互质的两部分。对于分母,互质的直接相乘,然后取模,对于分子,消掉和分母相同的部分,剩下的就是和m互质的那一部分了。然后进行扩展欧几里得求逆元。然后估计这里保存的分母的因子是一路计算来的因数的个数(幂数),所以一定会让分子只剩和m互质的部分,,,,,啊,其实因为对于公式,一定算出来是个整数【虽然没有证为什么】,但是由于取模了所以就可能不能整除了,所以在这里对分母进行分解,保留其幂数,然后在对分子的运算过程就可以借此消掉,好让结果还是个整数【虽然依旧没有证为什么……】
写出来主要是因为想了快两个小时才稍稍摸到门道,不想思路就这样没有了,然后……等我继续更新吧……这道题一定要A掉……
【诶,感觉自己蠢爆了= =】
【嗯,然后记得提醒自己,想不通就是想不通,好好想,不要以“可能是哪里没学过”这样的理由东看西看浪费时间,勇于尝试勇于想】
这是第一份代码,答案是对的,但是TLE了,试了一下数据,确实是跑得慢了,怎么优化都没有用。
#include <stdio.h>
#include <string.h>
#define maxn 500100
#define ll __int64
ll num[maxn],prim[maxn];
int fint[maxn];
int top;
int n,m;
/*void fint()
{
int i,j,k;
memset(su,1,sizeof(su));
su[0]=su[1]=0;
for(i=2;i<maxn;i++)
{
if(su[i])
for(j=i+i;j<maxn;j+=i)
su[i]=0;
}
}*/
void exgcd(ll a,ll b,ll &x,ll &y) ///模板
{
if(b==0)
{
x=1;
y=0;
}
else
{
ll t;
exgcd(b,a%b,x,y);
t=x; x=y;
y=t-(a/b)*y;
}
}
void cal1(ll &ans,ll x)
{
ll i,j,k;
j=x;
for(i=0;i<top;i++)
{
while(j%prim[i]==0)
{
j/=prim[i];
num[i]++;
}
}
ans=(ans)*(j);
ans%=m;
}
void cal2(ll &ans,ll x) ///取模可以对
{
int i,j,k;
j=x;
for(i=0;i<top;i++)
{
while((j%prim[i]==0)&&num[i]>0)
{
j/=prim[i];
num[i]--;
}
}
if(j>1)
{
ll xx,yy;
exgcd(j,m,xx,yy);
xx=(xx%m+m)%m; ///这一步很重要,有的时候逆元算出来貌似是负数,加这一步可以避免
ans=(ans*xx)%m;
}
}
ll quickpow(ll m,ll n,ll k)
{
ll b = 1;
while (n > 0)
{
if (n & 1)
b = (b*m)%k;
n = n >> 1 ;
m = (m*m)%k;
}
return b;
}
int main()
{
//int n,m;
freopen("out.txt","w",stdout);
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==1||n==2) {printf("0\n");continue;}
// memset(num,0,sizeof(num));
n-=2;
ll i,j,k;
j=m;
top=0;
for(i=2;i*i<=m;i++)
{
if(j%i==0)
{
prim[top++]=i;
while(j%i==0)
j/=i;
}
}
if(j>1) prim[top++]=j;
for(i=0;i<top;i++)
num[i]=0;
//h[1]=1;//h[2]=2;h[3]=5;
ll ans=1;
ll tmp;
for(i=2;i<=n;i++)
{
cal1(ans,4*i-2);
cal2(ans,i+1);
}
tmp=ans;
for(j=0;j<top;j++)
{
tmp*=quickpow(prim[j],num[j],m); ///这里改成用快速幂
/*for(k=0;k<num[j];k++)
tmp=(tmp*prim[j])%m;*/
}
printf("%I64d\n",tmp);
}
return 0;
}
嗯……学习了一下标程……
没有求逆元,没有递推式。
用的公式是这一种:h[n]=(2n!)/(n!*(n+1)!)
但是不知道为什么对于n的阶乘的因子分解是这样做的……效率高很多。然后素数记得开得大一点。
#include <stdio.h>
#include <string.h>
#define maxn 1000100
int prime[maxn],que[maxn];
int top;
int div(int x,int y) ///我不懂的因子分解……
{
int r=0;
//int tmp=x;
while(x)
{
r+=x/=y;
}
return r;
}
void fint()
{
int i,j,k;
memset(prime,1,sizeof(prime));
prime[0]=prime[1]=0;
for(i=2;i<maxn;i++)
{
if(prime[i])
for(j=i+i;j<maxn;j+=i)
prime[j]=0;
}
for(i=2;i<maxn;i++)
if(prime[i]) que[top++]=i;
}
int ppow(int a,int b,int m){
int r=1,t=a%m;
while(b){
if(b&1)r=(r*t)%m;
t=(t*t)%m;
b>>=1;
}
return r;
}
int solve(int n,int m)
{
int i,j,k;
int nn=n<<1;
//fint();
int r=1;
for(i=0;i<top&&que[i]<=nn&&r;i++)
{
//printf("**%d\n",que[i]);
j=div(nn,que[i]);//printf("%d ",j);
j-=div(n,que[i]);//printf("%d ",j);
j-=div(n+1,que[i]);//printf("%d\n",j);
r=(r*ppow(que[i],j,m))%m;
// printf("%d\n",r);
}
return r;
}
int main()
{
int n,m;
top=0;
fint();
//printf("%d\n",top);
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(scanf("%d%d",&n,&m)!=EOF)
{
printf("%d\n",solve(n-2,m));
}
return 0;
}
****************************************
估摸着需要鉴别一下两个公式的使用处。
第一个递推式就适合要求过程和的。
第二个式子就适合要求单个结果的。
1082

被折叠的 条评论
为什么被折叠?



