2021杭电多校1 HDU6956-Pass!【特征方程求解数列通向式+BSGS】
题目链接
赛中知道了\(f(i)=(n-2)*f(i-1)+(n-1)*f(i-2)\),然后考虑到矩阵上去了,然后歪了。
思路
看了题解才知道,可以将上式通过特征方程求解数列的通项公式求得
\(f(t)=\frac{((n-1)^t+(n-1)*(-1)^t)}{n}=x (\mod 998244353)\)
写一下是如何推导出来的。
已知\(a_i=(n-2)*a_{i-1}+(n-1)*a_{i-2}\),移项并构造一个类似等比数列的东西:
\(a_i-x*a_{i-1}=(a_{i-1}-x*a_{i-2})*y\),
那么可以发现
\(a_i=(x+y)*a_{i-1}-x*y*a_{i-2}\),
令\(c_1=x+y,c_2=-x*y\)其特征方程为:
\(x^2=c1*x+c2\) 即 \(x^2=(n-2)*x+(n-1)\)
求解可得 \(x_1=-1,x_2=n-1\)。
注意到原式子,代入\(x1,x2\)可通过
\(x+y=n-2,-x*y=n-1\)
求得\(y1=n-1,y2=-1\)。
由上文提到的这个式子 \(a_i-x*a_{i-1}=(a_{i-1}-x*a_{i-2})*y\) 通过移项得\(\frac{a_i-x*a_{i-1}}{a_{i-1}-x*a_{i-2}}=y\),这是一个首项为\(a_1-x*a_0\),公比为\(y\)得等比数列,即:
\(a_i-x*a_{i-1}=(a_1-x*a_0)*y^{i-1}\)
可列出方程组
得
\(a_i=\frac{(a_1-x_1*a_0)*y_1^{i-1}*x_2-(a_1-x_2*a_0)*y_2^{i-1}*x_1}{x_2-x_1}\)
已知\(a_0=1,a_1=0,x_1=-1,x_2=n-1,y_1=n-1,y_2=-1\),代入可得:
\(a_i=\frac{(n-1)^i+(n-1)*(-1)^i}{n}\)
那么就是式子\(f(t)=\frac{((n-1)^t+(n-1)*(-1)^t)}{n}\)
然后分奇偶BSGS即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef unsigned long long ULL;
#define x first
#define y second
const int N = 1e3 + 10, M = 13331;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
#define gcd __gcd
unordered_map<int, int> Hash;
LL kpow(LL a, LL n) {
LL res = 1;
while(n) {
if(n & 1) res = res * a % mod;
n >>= 1;
a = a * a % mod;
}
return res;
}
LL bsgs(LL a, LL b, LL p) {
Hash.clear();
a %= p; b %= p;
if(b == 1) return 0;
int k = sqrt(p) + 1;
for(int i = 0, j = b % p; i < k; i++) {
Hash[j] = i;
j = (LL)j * a % p;
}
int ak = kpow(a, k);
for(int x = 1, num = ak; x <= k; x++) {
if(Hash.count(num)) return x * k - Hash[num];
num = (LL)num * ak % p;
}
return -1;
}
void solve() {
LL n, x;
scanf("%lld%lld", &n, &x);
if(x == 1) puts("0");
else if(x == 0) puts("1");
else {
LL tmp = x;
x = 1LL * x * n % mod + (n - 1);
x = (x + mod) % mod;
LL res = bsgs(n - 1, x, mod);
if(res % 2 == 0) res = -1;
x = tmp;
x = 1LL * x * n % mod - (n - 1);
x = (x + mod) % mod;
LL ans = bsgs(n - 1, x, mod);
if(ans & 1) ans = -1;
if(res == ans && res == -1) puts("-1");
else if(res == -1) printf("%lld\n", ans);
else if(ans == -1) printf("%lld\n", res);
else printf("%lld\n", min(res, ans));
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
int t; cin >> t; while(t--)
solve();
return 0;
}