这是二阶齐次递推式,一个做法是构造二阶矩阵然后矩阵快速幂,复杂度为
O
(
8
∗
q
∗
log
(
1
0
9
)
)
O(8 * q * \log (10^9))
O(8∗q∗log(109)),T飞。
对于齐次线性递推式,可以推导它的通项公式:
令
F
(
n
)
=
x
n
F(n) = x^n
F(n)=xn:
F
(
n
)
−
3
∗
F
(
n
−
1
)
−
2
∗
F
(
n
−
2
)
=
0
F(n) - 3 * F(n - 1) - 2 * F(n - 2) = 0
F(n)−3∗F(n−1)−2∗F(n−2)=0
=
x
n
−
3
x
n
−
1
−
2
x
n
−
2
=
0
=x^n - 3 x ^{n-1} - 2x^{n-2}=0
=xn−3xn−1−2xn−2=0
x
2
−
3
x
+
2
=
0
x^2-3x+2=0
x2−3x+2=0可以解出两个特征根:
x
1
=
3
+
17
2
,
x
2
=
3
−
17
2
x_1 = \frac{3+\sqrt {17}}{2},x_2 = \frac{3-\sqrt {17}}{2}
x1=23+17,x2=23−17
那么 F ( n ) F(n) F(n) 的通解为: c 1 ∗ 3 + 17 2 + c 2 ∗ 3 − 17 2 c_1 * \frac{3+\sqrt {17}}{2} + c2 * \frac{3-\sqrt {17}}{2} c1∗23+17+c2∗23−17
c 1 ∗ x 1 + c 2 ∗ x 2 + . . + c n ∗ x n c_1 * x_1 + c_2 * x_2 + .. + c_n * x_n c1∗x1+c2∗x2+..+cn∗xn是 F ( n ) F(n) F(n)的通解仅当 x 1 , x 2 , . . . , x n x_1,x_2,...,x_n x1,x2,...,xn是 n 个不重复的特征根。
F ( 0 ) = 0 , F ( 1 ) = 1 F(0) = 0,F(1) = 1 F(0)=0,F(1)=1,带入可解出 c 1 = 17 17 , c 2 = − 17 17 c_1 = \frac{\sqrt{17}}{17},c_2 = -\frac{\sqrt{17}}{17} c1=1717,c2=−1717
F ( n ) = 17 17 ∗ ( ( 3 + 17 2 ) n − ( 3 − 17 2 ) n ) F(n) = \frac{\sqrt{17}}{17}*((\frac{3+\sqrt {17}}{2})^n - (\frac{3-\sqrt {17}}{2})^n) F(n)=1717∗((23+17)n−(23−17)n)
17
\sqrt{17}
17的二次同余展开为:
524399943
524399943
524399943
那么每次询问
l
o
g
(
1
0
9
)
log(10^9)
log(109)求快速幂即可,但由于
q
q
q达到了
1
0
7
10^7
107,这样还不够快。
对于 ( 3 + 17 2 ) n (\frac{3+\sqrt {17}}{2})^n (23+17)n和 ( 3 − 17 2 ) n (\frac{3-\sqrt {17}}{2})^n (23−17)n,可以分块打表记录,预处理 n = 1 , 2 , . . . , 1 0 9 n = 1,2,...,\sqrt{10^9} n=1,2,...,109 以及 n = 1 ∗ 1 0 9 , 2 ∗ 1 0 9 , . . . , 1 0 9 ∗ 1 0 9 n = 1 * \sqrt{10^9},2 * \sqrt{10^9},...,\sqrt{10^9}*\sqrt{10^9} n=1∗109,2∗109,...,109∗109的值,即可实现O(1)查询
代码:
#include<bits/stdc++.h>
using namespace std;
const int p = 524399943; //sqrt(17) mod 998244353 的二次剩余
const int mod = 998244353;
const int maxn = 1e5 + 10;
typedef long long ll;
ll fpow(ll a,ll b) {
ll r = 1;
while(b) {
if(b & 1) r = r * a % mod;
b >>= 1;
a = a * a % mod;
}
return r;
}
ll block,num;
ll mp1[maxn],np1[maxn];
ll mp2[maxn],np2[maxn];
ll inv2,invp;
ll cal(ll x) {
x %= (mod - 1);
ll t1 = np1[x / block] * mp1[x % block] % mod;
ll t2 = np2[x / block] * mp2[x % block] % mod;
return (t1 - t2 + mod) % mod * invp % mod;
}
ll n,q;
int main() {
inv2 = fpow(2,mod - 2);
invp = fpow(p,mod - 2);
ll a = (3 + p) * inv2 % mod,b = (3 - p + mod) % mod * inv2 % mod;
//分块打表,新技能
block = sqrt(mod);
num = mod / block + 1;
mp1[0] = mp2[0] = 1;
for(int i = 1; i <= block; i++) {
mp1[i] = mp1[i - 1] * a % mod;
mp2[i] = mp2[i - 1] * b % mod;
}
np1[0] = np2[0] = 1;
for(int i = 1; i <= num; i++) {
np1[i] = np1[i - 1] * mp1[block] % mod;
np2[i] = np2[i - 1] * mp2[block] % mod;
}
ll ans = 0,res = 0;
scanf("%lld%lld",&q,&n);
while(q--) {
n = n ^ (ans * ans);
ans = cal(n);
res ^= ans;
}
printf("%lld\n",res);
return 0;
}