Address
Solution
显然是斐波那契数列。
但是递推式加入了鬼畜的条件:
f
[
i
]
=
f
[
i
−
1
]
+
f
[
i
−
2
]
−
[
f
[
i
−
1
]
+
f
[
i
−
2
]
≡
1
(
 
m
o
d
 
k
)
]
f[i]=f[i-1]+f[i-2]-[f[i-1]+f[i-2]\equiv1(\bmod k)]
f[i]=f[i−1]+f[i−2]−[f[i−1]+f[i−2]≡1(modk)]
我们考虑对在模
k
k
k 意义下的
f
f
f 进行讨论。
可以发现,当
i
>
2
i>2
i>2 时,如果是满足
f
[
i
−
1
]
+
f
[
i
−
2
]
≡
1
(
 
m
o
d
 
k
)
f[i-1]+f[i-2]\equiv1(\bmod k)
f[i−1]+f[i−2]≡1(modk) 最小的
i
i
i ,
那么
f
[
i
]
=
0
f[i]=0
f[i]=0 ,而:
f
[
i
+
1
]
=
f
[
i
]
+
f
[
i
−
1
]
=
f
[
i
−
1
]
f[i+1]=f[i]+f[i-1]=f[i-1]
f[i+1]=f[i]+f[i−1]=f[i−1]
同样地,
f
[
i
+
2
]
=
f
[
i
+
1
]
+
f
[
i
]
=
f
[
i
−
1
]
f[i+2]=f[i+1]+f[i]=f[i-1]
f[i+2]=f[i+1]+f[i]=f[i−1] 。
于是接下去在没有模
k
k
k 余
1
1
1 的情况时,有
f
[
i
+
x
]
=
f
[
i
−
1
]
×
F
i
b
(
x
)
f[i+x]=f[i-1]\times Fib(x)
f[i+x]=f[i−1]×Fib(x) 。
(
F
i
b
(
x
)
Fib(x)
Fib(x) 为斐波那契数列第
x
x
x 项)
考虑如果存在最小
j
>
i
j>i
j>i 满足
f
[
j
−
1
]
+
f
[
j
−
2
]
≡
1
(
 
m
o
d
 
k
)
f[j-1]+f[j-2]\equiv1(\bmod k)
f[j−1]+f[j−2]≡1(modk) ,
那么有
f
[
i
−
1
]
×
F
i
b
(
j
−
i
)
≡
1
(
 
m
o
d
 
k
)
f[i-1]\times Fib(j-i)\equiv 1(\bmod k)
f[i−1]×Fib(j−i)≡1(modk) 。
用 exgcd 求出
f
[
i
−
1
]
f[i-1]
f[i−1] 的逆元
f
[
i
−
1
]
−
1
f[i-1]^{-1}
f[i−1]−1 ,找到一个最小的
j
>
i
j>i
j>i ,
满足
F
i
b
(
j
−
i
)
=
f
[
i
−
1
]
−
1
Fib(j-i)=f[i-1]^{-1}
Fib(j−i)=f[i−1]−1 ,则
f
[
j
−
1
]
+
f
[
j
−
2
]
≡
1
(
 
m
o
d
 
k
)
f[j-1]+f[j-2]\equiv1(\bmod k)
f[j−1]+f[j−2]≡1(modk) 。
如果
f
[
i
−
1
]
f[i-1]
f[i−1] 的逆元不存在或者不存在
F
i
b
(
j
−
i
)
=
f
[
i
−
1
]
−
1
Fib(j-i)=f[i-1]^{-1}
Fib(j−i)=f[i−1]−1 ,则在
i
i
i 之后不会出现模
k
k
k 余
1
1
1 的情况。
又由于
F
i
b
Fib
Fib 模
k
k
k 的余数的最小正周期为
O
(
k
)
O(k)
O(k) ,所以考虑先求出最小正周期
r
r
r ,然后预处理出
1
1
1 到
r
+
1
r+1
r+1 的
F
i
b
Fib
Fib 。
然后求出
F
i
b
[
1
…
r
+
1
]
Fib[1\dots r+1]
Fib[1…r+1] 的逆元
i
n
v
[
1
…
r
+
1
]
inv[1\dots r+1]
inv[1…r+1] 。
定义
F
i
b
x
Fib_x
Fibx 为:
F
i
b
x
(
1
)
=
F
i
b
x
(
2
)
=
x
Fib_x(1)=Fib_x(2)=x
Fibx(1)=Fibx(2)=x
F
i
b
x
(
n
)
=
F
i
b
x
(
n
−
1
)
+
F
i
b
x
(
n
−
2
)
,
n
>
2
Fib_x(n)=Fib_x(n-1)+Fib_x(n-2),n>2
Fibx(n)=Fibx(n−1)+Fibx(n−2),n>2
而定义
l
e
n
x
len_x
lenx 表示满足
l
e
n
x
>
2
len_x>2
lenx>2 且
F
i
b
x
(
l
e
n
x
)
≡
1
(
 
m
o
d
 
k
)
Fib_x(len_x)\equiv1(\bmod k)
Fibx(lenx)≡1(modk) 的最小的
l
e
n
x
len_x
lenx 。
由上面的推论得到,
如果
i
>
2
i>2
i>2 时
i
n
v
[
i
]
inv[i]
inv[i] 在
i
n
v
[
1...
i
−
1
]
inv[1...i-1]
inv[1...i−1] 中没有出现过,则
l
e
n
i
n
v
[
i
]
=
i
len_{inv[i]}=i
leninv[i]=i 。
其他的
l
e
n
len
len 都等于
∞
\infty
∞ 。
先设初始答案为
s
=
f
[
0
]
=
0
s=f[0]=0
s=f[0]=0 且设
x
=
n
u
m
=
1
x=num=1
x=num=1 。
其中
n
u
m
=
f
[
x
+
1
]
num=f[x+1]
num=f[x+1] (模
k
k
k 意义下),
s
s
s 表示
f
[
x
]
f[x]
f[x] (模
p
p
p 意义下),
x
x
x 表示当前讨论
f
[
x
+
1
…
x
+
l
e
n
n
u
m
]
f[x+1\dots x+len_{num}]
f[x+1…x+lennum] 。
循环进行下面算法:
(1)如果
l
e
n
n
u
m
≠
∞
len_{num}\ne\infty
lennum̸=∞ ,那么会有
f
[
x
+
l
e
n
n
u
m
]
=
0
f[x+len_{num}]=0
f[x+lennum]=0 。这时候可以用矩阵乘法将
s
s
s 从
f
[
x
]
f[x]
f[x] 转移到
f
[
x
+
l
e
n
n
u
m
]
f[x+len_{num}]
f[x+lennum] 。注意,如果
n
≤
f
[
x
+
l
e
n
n
u
m
]
n\le f[x+len_{num}]
n≤f[x+lennum] 则转移到
f
[
n
]
f[n]
f[n] 并输出即可。
然后
x
←
x
+
l
e
n
n
u
m
+
1
x\leftarrow x+len_{num}+1
x←x+lennum+1 ,
s
←
x
−
1
s\leftarrow x-1
s←x−1 ,
n
u
m
←
F
i
b
n
u
m
(
l
e
n
n
u
m
−
1
)
num\leftarrow Fib_{num}(len_{num-1})
num←Fibnum(lennum−1) 。
(2)否则
l
e
n
n
u
m
=
∞
len_{num}=\infty
lennum=∞ ,直接转移到
n
n
n 即可。
这样会 TLE 。但如果
n
u
m
num
num 出现了重复,就意味着
n
u
m
num
num 出现了循环节。通过一个循环节的转移系数相同。
如果循环节长度为
m
m
m ,要转移
n
n
n 次,就只需要预处理出通过一个循环节的转移矩阵
A
A
A 后,求矩阵
A
⌊
n
m
⌋
A^{\lfloor\frac nm\rfloor}
A⌊mn⌋ 后,剩下的
n
 
m
o
d
 
m
n\bmod m
nmodm 次暴力转移即可。
复杂度
O
(
k
log
)
O(k\log)
O(klog) 。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
template <class T>
T Min(T a, T b) {return a < b ? a : b;}
typedef long long ll;
const int N = 6e6 + 5, M = 3e6 + 5, E = 4;
int k, m, ZZQ, a[N], inv[M], tot, st[M], u[M], len[M], St, Ed, vis[M];
ll n, sum;
struct matr
{
int n, m, a[E][E];
matr() {}
matr(int _n, int _m) :
n(_n), m(_m) {memset(a, 0, sizeof(a));}
friend inline matr operator * (matr a, matr b)
{
int i, j, k;
matr res = matr(a.n, b.m);
For (i, 1, res.n) For (j, 1, res.m) For (k, 1, a.m)
res.a[i][j] = (res.a[i][j] + 1ll * a.a[i][k] * b.a[k][j] % ZZQ) % ZZQ;
return res;
}
friend inline matr operator ^ (matr a, ll b)
{
int i;
matr res = matr(a.n, a.m);
For (i, 1, res.n) res.a[i][i] = 1;
while (b)
{
if (b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
} A, D, J, V, K;
void exgcd(int a, int b, int &x, int &y)
{
if (!b) return (void) (x = 1, y = 0);
exgcd(b, a % b, y, x);
y -= a / b * x;
}
int getinv(int a, int ZZQ)
{
if (__gcd(a, ZZQ) != 1) return 0;
int x, y;
exgcd(a, ZZQ, x, y);
x = x % ZZQ + ZZQ;
return x >= ZZQ ? x - ZZQ : x;
}
matr frN(int l, int r, int st1, int st2, ll n)
{
int i;
D = matr(2, 1);
D.a[1][1] = st1; D.a[2][1] = st2;
For (i, l, r)
{
if (n <= 0) return D;
int rs = Min(n, 1ll * len[i]);
D = (A ^ rs) * D;
if (rs >= len[i] - 1) D.a[1][1] = (D.a[1][1] - 1 + ZZQ) % ZZQ;
if (rs == len[i]) D.a[2][1] = (D.a[2][1] - 1 + ZZQ) % ZZQ;
n -= rs;
}
return D;
}
int main()
{
int i;
cin >> n >> k >> ZZQ;
a[1] = a[2] = 1;
For (i, 3, 6000000)
a[i] = (a[i - 1] + a[i - 2]) % k;
For (i, 3, 6000000)
if (a[i] == 1 && a[i - 1] == 1)
{
m = i - 1; break;
}
For (i, 1, m) inv[i] = a[i] ? getinv(a[i], k) : 0;
For (i, 3, m) if (!vis[a[i]])
{
if (inv[i]) st[inv[i]] = i;
vis[a[i]] = 1;
}
memset(vis, 0, sizeof(vis));
u[1] = 1;
For (i, 2, k + 1)
{
len[i - 1] = st[u[i - 1]];
u[i] = 1ll * u[i - 1] * a[st[u[i - 1]] - 1] % k;
if (!u[i]) {St = i; Ed = -1; break;}
else if (vis[u[i]]) {St = vis[u[i]]; Ed = i - 1; break;}
vis[u[i]] = i;
}
A = matr(2, 2);
A.a[1][1] = A.a[2][1] = A.a[1][2] = 1;
For (i, 1, St - 1) sum += len[i];
matr firs = frN(1, St - 1, 1, 0, Min(n, sum));
if (n <= sum) return cout << firs.a[2][1] << endl, 0;
n -= sum; sum = 0;
if (Ed == -1) return cout << ((A ^ n) * firs).a[2][1] << endl, 0;
For (i, St, Ed) sum += len[i];
J = V = matr(3, 3);
V.a[1][1] = V.a[2][2] = V.a[3][3] = 1;
J.a[1][1] = J.a[1][2] = J.a[2][1] = 1; J.a[3][3] = 1;
K = J; K.a[1][3] = ZZQ - 1;
For (i, St, Ed)
V = J * K * (J ^ len[i] - 2) * V;
firs.n = 3; firs.a[3][1] = 1;
firs = (V ^ n / sum) * firs;
firs = frN(St, Ed, firs.a[1][1], firs.a[2][1], n % sum);
cout << firs.a[2][1] << endl;
return 0;
}