[做题记录-乱做] BZOJ 3328 PYXFIB
题意
求
\[\sum_{i = 0}^{n} \binom{n}{i}f_i[k | i]
\]
\(n \leq 10^{18}, k \leq 20000, T \leq 20\)
\(f\)是斐波那契数。
题解
单位根反演
\[[k|n] = \frac{1}{k}\sum_{i = 0}^{k - 1}w_k^{ni}
\]
找原根
-
分解\(P - 1\)
-
枚举一个数\(a\)
-
对于每一个质因子\(i\), 判断\(a^{(P - 1)/ i}\)是否为\(1\)
-
如果对于每一个质因子上面的测试都不是\(1\), 那就找到了一个原根\(a\)。
-
2的原根是\(1\)。
-
原根的所有幂次在剩余系下的取值给出所有原根。
题解
搞个矩阵\(A\)表示斐波那契的转移矩阵, 考虑化式子。
\[\sum_{i = 0}^n\binom{n}{i}f_i(\frac{1}{k}\sum_{j = 0}^{k - 1}w_k^{ij}) \\
=\frac{1}{k}\sum_{j = 0}^{k - 1}\sum_{i = 0}^n\binom{n}{i}(Aw_{k}^j)^i \\
=\frac{1}{k}\sum_{j = 0}^{k - 1}(Aw_k^j+I)^n
\]
求出原根然后算就完了。
#include <bits/stdc++.h>
using namespace std;
namespace IO {
char a; int f;
template<typename T> inline void read(T &x) {
x = 0; f = 1; a = getchar();
for(; ! isdigit(a); a = getchar()) if(a == '-') f = -1;
for(; isdigit(a); a = getchar()) x = x * 10 + a - '0';
x *= f;
}
template <typename T> inline void write(T x) {
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using IO :: read;
using IO :: write;
int P = 998244353;
inline int mod(int x) { return x + ((x >> 31) & P); }
inline void pls(int &x, int y) { x = mod(x + y - P); }
inline void dec(int &x, int y) { x = mod(x - y); }
inline int power(int x, int k) {
int res = 1;
// cerr << k << endl;
while(k) {
if(k & 1) res = 1ll * res * x % P;
x = 1ll * x * x % P; k >>= 1;
} return res;
}
#define lep(i, l, r) for(int i = (l); i <= (r); i ++)
#define rep(i, l, r) for(int i = (l); i >= (r); i --)
using i64 = long long;
using i128 = __int128;
using uint = unsigned int;
using ui64 = unsigned long long;
struct Mat {
int a[2][2];
Mat() { a[0][0] = a[0][1] = a[1][0] = a[1][1] = 0; }
Mat(int _a, int _b, int _c, int _d) {
a[0][0] = _a; a[0][1] = _b; a[1][0] = _c; a[1][1] = _d;
}
inline Mat operator *(const Mat &t) const {
Mat r;
lep (i, 0, 1)
lep (j, 0, 1)
lep (k, 0, 1)
r.a[i][j] = (r.a[i][j] + 1ll * a[i][k] * t.a[k][j]) % P;
return r;
}
inline Mat operator ^(i64 k) {
Mat res(1, 0, 0, 1);
Mat x = *this;
while(k) {
if(k & 1) res = res * x;
x = x * x; k >>= 1;
}
return res;
}
} ;
const Mat I = Mat(1, 0, 0, 1);
int k, p;
i64 n;
int findroot(int p) {
if(p == 2) return 1;
vector<int> factor;
int t = p;
p --;
for(int i = 2; i * i <= p; i ++)
if(p % i == 0) {
factor.push_back(i);
while(p % i == 0) p /= i;
}
if(p > 1) factor.push_back(p);
//cerr << factor.size() << endl;
p = t;
lep (a, 2, p - 1) {
int fail = 0;
lep (j, 0, factor.size() - 1) {
int i = factor[j];
//cerr << i << endl;
//cerr << power(a, (P - 1) / i) << endl;
// cerr << a << ' ' << i << ' ' << power(a, (p - 1) / i) << endl;
if(power(a, (P - 1) / i) == 1) { fail = 1; break; }
}
if(! fail) return a;
}
return -1;
}
void solve() {//return ;
read(n); read(k); read(p);
P = p; //cerr << P << endl;
int g = findroot(p); //cerr << g << endl;
int w = power(g, (P - 1) / k);
int ans = 0;
Mat A(1, 1, 1, 0);
int W = 1;
lep (j, 0, k - 1) {
Mat F = A;
lep (x, 0, 1)
lep (y, 0, 1)
F.a[x][y] = 1ll * F.a[x][y] * W % P;
F.a[0][0] ++; F.a[1][1] ++;
F = F ^ n;
ans = (ans + F.a[0][0]) % P;
W = 1ll * W * w % P;
}
ans = 1ll * ans * power(k, P - 2) % P;
write(ans);
putchar('\n');
}
signed main() {
//freopen(".in", "r", stdin);
//freopen(".out", "w", stdout);
// P = p;
// cerr << findroot(5) << endl;
// return 0;
int Case; read(Case);
while(Case --) solve();
return 0;
}