题目大意:求从杨辉三角顶端往
(n,k)
走的路径上元素之和的最小值。
(0<=k<=n<109)
多组测试数据。
找找规律就知道当
n−k<k
时,答案为
Cn+1k+1+k
,反之答案为
Cn+1k+n−k
看起来很简单,实际上单纯地这样做是会无限TLE的……
有一个用于大整数组合数的定理:Lucas定理
计
Cmn
%
p=Lucas(n,m,p)
即
Lucas(n,m,p)=Cab∗Lucas(n/p,m/p,p)
其中
a≡n(mod
p)
b≡m(mod
p)
证明:以求n!%p为例,每p个为一段,而每一段求的结果一样。但需要单独处理末尾以及
p,2∗p,3∗p,4∗p
…
n/p
将p提取出来,剩下的数正是
(n/p)!
然而这道题还是会TLE…
再加上阶乘和逆元的预处理就完美地AK了 O(∩_∩)O~
对逆元计算有很多方法,在这里推荐一种递推的方法:
ni[i]=−p/i∗ni[p
%
i]
在木有看题解之前,蒟蒻我也是无限TLE…
蒟蒻还需加油 ↖(^ω^)↗
附代码
#include<iostream>
#include<cstdio>
#define LL long long int
using namespace std;
const int MAXN=10000;
LL x0,y0;
int d,flag[MAXN],tmp[MAXN],prime[MAXN];//用于筛素数
int fac[1230][MAXN];//计算阶乘
int niyuan[1230][MAXN];//逆元
int a,b,p,ans,pos;
void gcd(int a,int b,int &d,LL &x0,LL &y0)
{
if(!b)
d=a,x0=1,y0=0;
else
{
gcd(b,a%b,d,y0,x0);
y0-=x0*(a/b);
}
}
void init()
{
pos=0;
for(int i=2;i<MAXN;++i)
{
if(!flag[i])
{
prime[++pos]=i;
tmp[i]=pos;
}
for(int j=1;j<=pos&&i*prime[j]<MAXN;++j)
{
flag[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
for(int i=1;i<=pos;++i)
{
int k=prime[i];
fac[i][0]=fac[i][1]=niyuan[i][0]=niyuan[i][1]=1;
for(int j=2;j<k;++j)
{
fac[i][j]=(fac[i][j-1]*j)%prime[i];
gcd(fac[i][j],k,d,x0,y0);
niyuan[i][j]=(x0%k+k)%k;
}
}
}
int cal(int a,int b)
{
if(a<b)
return 0;
int pos=tmp[p];
return fac[pos][a]*niyuan[pos][b]%p*niyuan[pos][a-b];
}
int lucas(int n,int m)
{
LL ans=1;
while(n&&m&&ans)
{
ans=(ans*cal(n%p,m%p))%p;
n/=p;
m/=p;
}
return ans;
}
int main()
{
int cnt=0;
init();
while(scanf("%d%d%d",&a,&b,&p)!=EOF)
{
if(a-b<b)
printf("Case #%d: %d\n",++cnt,(lucas(a+1,b+1)+b)%p);
else
printf("Case #%d: %d\n",++cnt,(lucas(a+1,b)+a-b)%p);
}
return 0;
}