1 题意
给定一个杨辉三角:
再给定杨辉三角中的一个点
(
n
,
m
)
(n,m)
(n,m),要求从
(
0
,
0
)
(0,0)
(0,0)出发,按照
f
i
g
u
r
e
2
figure 2
figure2所示两个方向行走,走到
(
n
,
m
)
(n,m)
(n,m)所需要的最小花费(花费等于经过数字之和,不包括
(
0
,
0
)
(0,0)
(0,0))。
链接:link。
2 思路
有一个很明显的贪心思想:在杨辉三角中,两侧的数字总是比中间的小,所以行走的时候尽量靠边行走即可。
根据上述贪心思想可以得出:当
m
⩽
⌈
n
2
⌉
m \leqslant \lceil \frac{n}{2} \rceil
m⩽⌈2n⌉时,从
(
0
,
0
)
(0,0)
(0,0)到
(
n
,
m
)
(n,m)
(n,m)的最小花费为
(
n
m
)
+
(
n
−
1
m
−
1
)
+
.
.
.
+
(
n
−
m
0
)
+
n
−
m
\binom{n}{m}+\binom{n-1}{m-1}+...+\binom{n-m}{0}+n-m
(mn)+(m−1n−1)+...+(0n−m)+n−m。
利用
P
a
s
c
a
l
Pascal
Pascal公式
(
n
−
1
m
)
+
(
n
−
1
m
−
1
)
=
(
n
m
)
\binom{n-1}{m}+\binom{n-1}{m-1}=\binom{n}{m}
(mn−1)+(m−1n−1)=(mn)对上式化简:
原式
=
(
n
m
)
+
(
n
−
1
m
−
1
)
+
.
.
.
+
(
n
−
m
+
1
0
)
+
n
−
m
=
(
n
+
1
m
)
+
n
−
m
=\binom{n}{m}+\binom{n-1}{m-1}+...+\binom{n-m+1}{0}+n-m=\binom{n+1}{m}+n-m
=(mn)+(m−1n−1)+...+(0n−m+1)+n−m=(mn+1)+n−m。
当
m
>
⌈
n
2
⌉
m > \lceil \frac{n}{2} \rceil
m>⌈2n⌉时,由对称性,转而求
n
−
m
n-m
n−m即可。
最后利用
L
u
c
a
s
Lucas
Lucas公式求解:
(
s
p
+
q
t
p
+
r
)
≡
(
s
t
)
(
q
r
)
(
m
o
d
(
p
)
)
\left(\begin{array}{l} s p+q \\ t p+r \end{array}\right) \equiv\left(\begin{array}{l} s \\ t \end{array}\right)\left(\begin{array}{l} q \\ r \end{array}\right)(\bmod (p))
(sp+qtp+r)≡(st)(qr)(mod(p))。
在利用
L
u
c
a
s
Lucas
Lucas公式求解组合数时,会涉及到计算
(
q
r
)
(
0
⩽
r
⩽
q
<
p
)
\binom{q}{r}(0 \leqslant r \leqslant q < p)
(rq)(0⩽r⩽q<p),可以事先把
x
!
(
0
⩽
x
<
p
)
x!(0 \leqslant x < p)
x!(0⩽x<p)在模
p
p
p情况下的值及其逆元求出来(由于
(
0
⩽
x
<
p
)
(0 \leqslant x < p)
(0⩽x<p),所以
x
!
x!
x!的逆元一定存在),之后便能在
O
(
1
)
\mathcal{O}(1)
O(1)的时间求出
(
q
r
)
\binom{q}{r}
(rq)。
2.1 时间复杂度分析
预处理中,对每个素数 p p p,都要计算 x ! ( 0 ⩽ x < p ) x!(0 \leqslant x < p) x!(0⩽x<p)的模和逆元,所以时间复杂度为 m a t h c a l O ( n + p log ( p ) ϕ ( n ) ) mathcal{O}(n+p\log(p)\phi(n)) mathcalO(n+plog(p)ϕ(n)),而 L u c a s Lucas Lucas计算的时间复杂度为 O ( log ( n ) ) \mathcal{O}(\log(n)) O(log(n)),假设要询问 t t t次,那么总的时间复杂度为 O ( n + p log ( p ) ϕ ( n ) + t log ( n ) ) \mathcal{O}(n+p\log(p)\phi(n)+t\log(n)) O(n+plog(p)ϕ(n)+tlog(n))。
2.2 实现
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e4+10;
const int M=1300;
int pos[N]={0},rvs[M][N]={0},ftr[M][N]={0},prm[N],vis[N],cnt=0;
int qpow(int a,int b,int p){
int res=1,rem=a;
while(b){
if(b&1) res=res*rem%p;
rem=rem*rem%p;
b>>=1;
}
return res;
}
void init(){
for(int i=2;i<N;i++){
if(!vis[i]){
prm[cnt++]=i;
pos[i]=cnt;
ftr[cnt][0]=rvs[cnt][0]=1;
for(int j=1,k=1;j<i;j++){
k=k*j%i;
ftr[cnt][j]=k;
rvs[cnt][j]=qpow(k,i-2,i);
}
}
for(int j=0;j<cnt&&i*prm[j]<N;j++){
vis[i*prm[j]]=1;
if(i%prm[j]==0) break;
}
}
}
int combination(int n,int m,int p){
if(m>n) return 0;
int idx=pos[p];
return ftr[idx][n]*rvs[idx][m]%p*rvs[idx][n-m]%p;
}
int Lucas(int n,int m,int p){
if(m==0) return 1;
return combination(n%p,m%p,p)*Lucas(n/p,m/p,p)%p;
}
int main(){
int k=1,n,m,p;
init();
while(~scanf("%d %d %d",&n,&m,&p)){
if(2*m>n) m=n-m;
printf("Case #%d: %d\n",k++,((Lucas(n+1,m,p)+n-m)%p+p)%p);
}
return 0;
}