Lucas定理及其在递推关系式中的作用

在 ACM 竞赛中,由于 C++ 库中没有大数类,手写大数类有些麻烦且写好类接口再应用仍然很麻烦,因此我们经常在有限域 $\mathbb{Z} / p \mathbb{Z}$ 中考虑问题。这里介绍一下 Lucas 定理,及在一类递推关系式中的应用。

Lucas定理

对任意非负整数 $m,n$ 和素数 $p$,我们有:
$$ C_{m} ^ n = \prod_{i=0} ^k C_{m_i} ^{n_i} \mod p$$
其中 $m = \sum_{i=0} ^k m_i p^i,n = \sum_{i=0} ^k n_i p^i$。
Proof: 由组合数的定义
$$ C_{p} ^ n = \frac{p(p-1) \cdots (p-n+1)}{n(n-1) \cdots 1} $$
容易看出 $p \mid n! \, C_{p} ^ n $, 因此当 $n<p$ 时,$p \mid C_{n} ^ p $。即我们有
$$ (1+x)^p = 1 + x^p \mod p$$
因此
$$ \begin{align}
\sum_{n=0}^m C_{m}^n x^n &= (1+x)^m = \prod_{i=0} ^k \left( (1+x)^{p^i} \right)^{m_i} \\
&= \prod_{i=0}^k \left( 1+x^{p^i} \right)^{m_i} = \prod_{i=0}^k \left( \sum_{n_i=0}^{m_i} C_{n_i}^{m_i} x^{n_ip^i} \right) \\
&= \sum_{n=0}^m \left(\prod_{i=0} ^k C_{m_i} ^{n_i} \right) x^n \mod p
\end{align} $$
由多项式对应系数相等,证毕。

一类递推关系决定的数列

已知 $f(n) = (a+\sqrt{b})^n + (a-\sqrt{b})^n$ 则容易知道其可由 $f(0)=2,f(1)=2a$, $f(n)=2af(n-1)+(b-a^2)f(n-2)$ 相互决定。现在我们要计算 $f(n) \mod p$,其中 $p$ 是素数,如果 $b$ 是 $p$ 的二次剩余,就可以直接计算,并且可以看出此时 $f(n)$ 的周期是 $p-1$。否则,我们用矩阵的策略来计算,这个已经是套路了,可看我之前的博文
令 $ A = \left[ \begin{matrix} 2a & b-a^2 \\ 1 & 0 \end{matrix} \right] $ 则
$$ \left( \begin{matrix} f(n+1) \\ f(n) \end{matrix} \right) = A \left( \begin{matrix} f(n) \\ f(n-1) \end{matrix} \right) = A^n \left( \begin{matrix} 2a \\ 2 \end{matrix} \right) $$

即我们只需计算 $A^n$ 即可。并且我们知道 可逆矩阵 $A^n$ 的周期是 $(p^2-1)(p^2-p)$。问题在于若 $p=1e9+7$ 这个周期要用 $__int128$ 来存储(其实这也是可以的,但是如果 $p=1e18+9$ 呢)。对于刚过所说的这种特殊情况。其实此时 $A$ 的周期(不一定是最小)为 $p^2-1$。实际上我们只要说明 $f(n)$ 的周期是 $p^2-1$ 即可。但对于一般的 $A$ 并不清楚。

因为 $f(p^2)$ 展开后不含 $\sqrt{b}$ 项,且由 Lucas 定理知道,$C_{p^2}^i,0 \leq i \leq p^2$ 中,非0数仅有 $C_{p^2}^0 = C_{p^2}^{p^2} = 1 \mod p$。因此 $f(p^2) = 2a^{p^2} = 2a \mod p$。
类似地,$C_{p^2+1}^i,0 \leq i \leq p^2+1$ 中,非0数仅有 $C_{p^2+1}^0,C_{p^2+1}^1,C_{p^2+1}^{p^2},C_{p^2+1}^{p^2+1} \mod p$。因此:
$$ f(p^2+1) = 2(a^{p^2+1} + (\sqrt{b})^{p^2+1})= 2(a^2+b) = f(2)$$
由$f(n)$的取值由前两项(相邻两项,后两项)唯一确定,因此$f(n+p^2-1) = f(n),n>=0$。
上面这一段证明来自 Bestcoder 81。更简单的证明见这里

注意事项

这类问题精彩需要注意的是:

  1. 矩阵中元素本身是p的倍数时很容易出问题。
  2. 不要溢出,不要出现负数

HDU 5674

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//#pragma comment(linker,"/STACK:10240000,10240000")
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
#define clr(a,b) memset(a,b,sizeof(a))
#define MP make_pair
#define PB push_back
#define lrt rt<<1
#define rrt rt<<1|1
#define lson l,m,lrt
#define rson m+1,r,rrt
/*------ Welcome to visit blog of dna049: http://dna049.com ------*/
const LL M = 1e9+7;
LL mod = M;
class Matrix{
public:
const static int N = 2; //col
LL a[N][N];
Matrix(LL a00=0,LL a01=0,LL a10=0,LL a11=0){
a[0][0]=a00;a[0][1]=a01;
a[1][0]=a10;a[1][1]=a11;
}
Matrix operator+(const Matrix& A)const{
Matrix R;
for(int i=0;i<N;++i){
for(int j=0;j<N;++j){
R.a[i][j]=a[i][j]+A.a[i][j];
if(R.a[i][j]>=mod) R.a[i][j]-=mod;
}
}
return R;
}
Matrix operator*(const Matrix& A)const{
Matrix R;
for(int i=0;i<N;++i){
for(int k=0;k<N;++k){
for(int j=0;j<N;++j){
R.a[i][j] = (R.a[i][j]+a[i][k]*A.a[k][j])%mod;
}
}
}
return R;
}
};
Matrix pow(Matrix A,LL n){
Matrix R(1,0,0,1);
while(n){
if(n&1) R=R*A;
n>>=1; A=A*A;
}
return R;
}
LL mul_mod(LL x,LL n,LL p){
LL r=0;
while(n){
if(n&1) r=(r+x)%p;
n>>=1; x=(x+x)%p;
}
return r;
}
LL pow_mod(LL x,LL n,LL p){
LL r=1;
while(n){
if(n&1) r=mul_mod(r,x,p);
n>>=1; x=mul_mod(x,x,p);
}
return r;
}
int main(){
// freopen("/Users/dna049/Desktop/AC/in","r",stdin);
// freopen("/Users/dna049/Desktop/AC/out","w",stdout);
int T;
cin>>T;
while(T--){
LL a,b,x,y;
cin>>a>>b>>x>>y;
Matrix A((a*2)%mod,((b-a*a)%mod+mod)%mod,1,0);
A = pow(A,pow_mod(x,y,M*M-1));
cout<<(A.a[1][0]*a*2+A.a[1][1]*2)%mod<<endl;
}
return 0;
}