相关知识。
给定一个长度为 k 的数列 b1,b2,…,bk。
有一个长度为 n 的数列 f,满足 f1=f2=⋯=fk−1=1,fn=m。
且当 i>k 时有递推式:
fi=(k∏j=1fbji−j)mod
求 f_k 的一个可能值,k \leq 100, n \leq 10^9。
k 很小,n 很大,式子长得也很像矩阵优化。
问题是我们只会用矩阵优化形如这样的式子:
f_i = \sum\limits_{i=1}^{k} f_{i-j} \times b_j
考虑能不能把题目中的递推式做一个类似于降次的转化,使得乘法变成加法,次幂变成乘法,这样就可以矩阵优化了。
基于这个思路,很容易想到取对数。
但是这是在模 998244353 意义下,哪来的实数对数?
离散对数。
众所不周知,mod = 998244353 是一个质数,且它的最小原根为 3,\varphi(mod) = mod - 1。
由 这里 6.4 得:3 在对 mod 取模意义下的次幂 \{3^0, 3^1, \dots, 3^{mod - 1}\} 可以表示 [0,mod) 的任何一个数。
考虑设 f_i 的离散对数是 g_i,这下就有:
g_i = ( \sum\limits_{i=1}^{k} g_{i-j} \times b_j ) \bmod 998244352
这可以矩阵优化了,通过矩阵快速幂可以很轻松地算出 g_n。
初始矩阵 \Big[ 0, 0, \dots, g_k \Big],但是发现我们并不知道 g_k 的值。
但相反地我们竟然可以通过 BSGS 算出 g_n 的值,因为 f_n 已知,具体地,3^{g_n} \equiv f_n \pmod {998244353}。
考虑钦定 g_k = 1 再做矩乘,假设得到 g_n 是 g_k 的 t 倍,那么 g_n \equiv t \times g_k \pmod {998244352}。
由于 998244352 不是质数,因此需要用 exgcd 求逆元。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 105, mod = 998244353;
int k, n, fn, gn, gk, t, b[N];
struct Mat {
int a[N][N];
void init() {
for (int i = 1; i <= k; i++)
for (int j = 1; j <= k; j++) a[i][j] = 0;
}
} ;
Mat mul(Mat a, Mat b) {
Mat res; res.init();
for (int i = 1; i <= ::k; i++)
for (int j = 1; j <= ::k; j++)
for (int k = 1; k <= ::k; k++)
(res.a[i][j] += a.a[i][k] * b.a[k][j] % (mod - 1) ) %= (mod - 1);
return res;
}
Mat qmi(Mat a, int k) {
Mat res; res.init();
for (int i = 1; i <= ::k; i++) res.a[i][i] = 1;
while (k) {
if (k & 1) res = mul(res, a);
a = mul(a, a), k >>= 1;
}
return res;
}
unordered_map<int, int> mp;
int BSGS(int a, int b, int p) {
mp.clear();
int mul = 1, now = 1, B = sqrt(p) + 1;
for (int i = 0; i <= B; i++) {
if (mul == b) return i;
if (!mp.count(b * mul % p)) mp[b * mul % p] = i;
if (i < B) mul = mul * a % p;
}
for (int j = 1; j <= B; j++) {
now = now * mul % p;
if (mp.count(now)) return j * B - mp[now];
}
return -1;
}
int gett() { // 求 gn = t * gk (mod p) 的 t ----> gn
Mat e, f; e.init(), f.init();
f.a[1][k] = 1;
for (int i = 1; i < k; i++) e.a[i + 1][i] = 1;
for (int i = 1; i <= k; i++) e.a[i][k] = b[k - i + 1];
return mul(f, qmi(e, n - k) ).a[1][k];
}
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
int exgcd(int a, int b, int &x, int &y) {
if (!b) { x = 1, y = 0; return a; }
int d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
int inv(int n, int m) {
int x, y, d = exgcd(n, m, x, y);
return (x % m + m) % m;
}
int getans(int gn, int t) {
// cout << '\t' << gn << ' ' << t << endl;
int G = gcd(t, mod - 1); //两边同除 G
if (gn % G) return -1;
return (gn / G) * inv(t, mod - 1) % (mod - 1); // inv(t / G) 假了。
}
int qmii(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = res * 1ll * a % mod;
a = a * 1ll * a % mod, k >>= 1;
}
return res;
}
signed main() {
scanf("%lld", &k);
for (int i = 1; i <= k; i++) scanf("%lld", &b[i]);
scanf("%lld%lld", &n, &fn);
gn = BSGS(3, fn, mod), t = gett();
gk = getans(gn, t);
// cout << gk << endl;
if (gk == -1) return puts("-1"), 0;
printf("%lld\n", qmii(3, gk) );
return 0;
}