【bzoj3328】PYXFIB 题解

题目大意

  求:

i=0nkCnikFik(modp)\sum_{i=0}^{\lfloor \frac{n}{k} \rfloor} C_{n}^{ik}F_{ik} \pmod p

  其中 FF 表示斐波那契数列,F[0]=F[1]=1F[0]=F[1]=1

  n1018, k20000, p109n \leq 10^{18},\ k \leq 20000,\ p \leq 10^9 且保证 pp 为质数、pmodk=1p\bmod k=1

题解

  首先斐波那契数列可以用矩阵来表示,设为 A=[1110]A=\begin{bmatrix} 1&1 \\ 1&0\end{bmatrix},则 F(i)=AiF(i)=A^i 的左上角。
  于是原式等于

i=0nkCnikAik(modp)\sum_{i=0}^{\lfloor \frac{n}{k} \rfloor} C_n^{ik}A^{ik} \pmod p

  上标为 ikik 很难看,于是换种写法:

i=0n [imodk=0]CniAi(modp)\sum_{i=0}^n ~[i \bmod k=0]C_n^i A^i \pmod p

  现在考虑怎么替换 [imodk=0][i \bmod k=0] 这个条件。(据说这个东西叫单位根反演?)
  令 pp 的原根为 gg,设 w=gp1kw=g^{\frac{p-1}{k}},我们来观察一下 j=0k1wij\sum_{j=0}^{k-1}w^{ij}的性质。
  1、imodk=0i \bmod k=0:则 wijw^{ij} 恒等于 11,因此原式 =k=k
  2、imodk0i \bmod k\not=0:则由等比数列求和得:原式 =wik1wi1=11wi1=0=\frac{w^{ik}-1}{w^i-1}=\frac{1-1}{w^i-1}=0
  综上,[imodk=0][i \bmod k=0] 可以替换成 1kj=0k1wij\frac{1}{k}\sum_{j=0}^{k-1}w^{ij}
  (超厉害。。。)
  于是现在原式变成:

i=0nCniAi1kj=0k1wij(modp)\sum_{i=0}^n C_n^i A^i \frac{1}{k}\sum_{j=0}^{k-1}w^{ij} \pmod p

  发现 CniC_n^i 乘上一堆上标为 ii 的东西很像二项式展开,于是交换一下顺序变成:

1kj=0k1i=0nCniAiwij(modp)=1kj=0k1(Awj+I)n(modp)\begin{aligned} &\frac{1}{k}\sum_{j=0}^{k-1}\sum_{i=0}^n C_n^i A^i w^{ij} \pmod p \\ =&\frac{1}{k}\sum_{j=0}^{k-1}(Aw^j+I)^n \pmod p \end{aligned}

  其中 II 表示单位矩阵。

  然后,枚举 jj 就做完了。

代码

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
98
99
100
#include<cmath>
#include<cstdio>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;

const int maxsqrtp=5e4+5;

struct Arr{
LL n[2][2];
};

LL n,p;
int k;

LL mi(LL x,LL y)
{
LL re=1;
for(; y; y>>=1, (x*=x)%=p) if (y&1) (re*=x)%=p;
return re;
}

int g,p0,pr[maxsqrtp];
void find_g()
{
int sqrtp=sqrt(p), pp=p-1;
p0=0;
fo(i,2,sqrtp) if (pp%i==0)
{
pr[++p0]=i;
while (pp%i==0) pp/=i;
}
if (pp>1) pr[++p0]=pp;
for(g=2; ; g++)
{
bool pd=1;
fo(i,1,p0) if (mi(g,(p-1)/pr[i])==1) {pd=0; break;}
if (pd) return;
}
}

Arr F,I;
Arr mul(Arr a,Arr b)
{
Arr re;
re.n[0][0]=(a.n[0][0]*b.n[0][0]+a.n[0][1]*b.n[1][0])%p;
re.n[0][1]=(a.n[0][0]*b.n[0][1]+a.n[0][1]*b.n[1][1])%p;
re.n[1][0]=(a.n[1][0]*b.n[0][0]+a.n[1][1]*b.n[1][0])%p;
re.n[1][1]=(a.n[1][0]*b.n[0][1]+a.n[1][1]*b.n[1][1])%p;
return re;
}
Arr mulnum(Arr a,LL b)
{
(a.n[0][0]*=b)%=p;
(a.n[0][1]*=b)%=p;
(a.n[1][0]*=b)%=p;
(a.n[1][1]*=b)%=p;
return a;
}
Arr plusI(Arr a)
{
(a.n[0][0]+=1)%=p;
(a.n[1][1]+=1)%=p;
return a;
}
Arr Mi(Arr x,LL y)
{
Arr re=I;
for(; y; y>>=1, x=mul(x,x)) if (y&1) re=mul(re,x);
return re;
}

int T;
int main()
{
F.n[0][0]=F.n[0][1]=F.n[1][0]=1;
F.n[1][1]=0;
I.n[0][0]=I.n[1][1]=1;

scanf("%d",&T);
while (T--)
{
scanf("%lld %d %lld",&n,&k,&p);

find_g();
LL w0=mi(g,(p-1)/k);

LL ans=0, w=1;
fo(j,0,k-1)
{
Arr t=Mi(plusI(mulnum(F,w)),n);
(ans+=t.n[0][0])%=p;
(w*=w0)%=p;
}

printf("%lld\n",ans*mi(k,p-2)%p);
}
}