题解 【YZOJ P4226 [校内训练20190117]数组长度问题】
\(\large\mathcal{Description}\)
知道 \(k,b,x_0,p\) 的情况下,所有在 \(\bmod p\) 意义下等于 \(x_0\),并且写成 \(k\) 进制数后形如 \(\overline{x_0bbbbbbb\ldots}\) 的数中,严格大于 \(x_0\) 的最小的那一个数在 \(k\) 进制下有多少位等于 \(b\)。这个值记为 \(n\)。
特别地,如果不存在一个严格大于 \(x_0\) 的数满足这些条件,令 \(n=-1\)。
求 \(n\).
- \(2 \le k < p \le 19260817\)
- \(0 \le b < k\)
- \(1 \le x_0 < k\)
- 多测,测试组数 \(\le 100000\)
\(\large\mathcal{Solution}\)
或许是这道题最详细的题解了。
我们可以先处理掉 \(k=0, 1\) 的情况,此处略去不谈,下文默认 \(k\ge 2\).
首先易知
我们设 \(R = (k−1)\times x_0+b\), 那么就有
这里我们可以把 \(\gcd(R, p)\) 处理掉,具体的:
设 \(q = \dfrac{p}{\gcd(R, p)}\).
- 若 \(q = 1\), 就有 \(r = 1 + [x_0 = b]\).(注:\([x]\) 表示若 \(x\) 为真,则为 \(1\); 否则为 \(0\))
- 若 \(q \ge 2\),就有
那么肯定有
即:\(n\) 为 \(k\) 关于\(\bmod q\) 的阶。
又有
根据阶的性质,就有: \(n\) 是 \(φ(q)\) 的因数。 因此我们只要遍历 \(φ(q)\) 的因数,再依次检查是否 \(k^n-1\equiv 0 \bmod q\) 即可。
同时我们要检查是否 \(1+k+k^2+k^3+...k^{n−1}\equiv 0\bmod q\),因为 \(k^n\equiv 1\bmod q\) 只是他的必要条件。
这里提供一种 \(\mathcal{O}(\log n)\) 计算 \((1+k+k^2+k^3+...k^{n−1})\bmod q\) 的方法。
方便起见,令 \(sum(p, c) = 1+p+p^2+...+p^c\).
考虑分治法:
若 \(sum(k, n-1)\bmod q=0\), \(n + [x_0=b]\) 即为答案。
否则答案是 \(n\times \dfrac{q}{\gcd(q, sum(k, n-1))} + [x_0=b]\).
代码实现的时候记得:
- 答案要加上 \([x_0=b]\), 否则 WA.
- 筛 \(φ\) 函数的时候要用线性筛,否则 TLE.
\(\mathcal{Code}\)
#include <bits/stdc++.h>
#define reg register
using namespace std;
typedef long long LL;
const int N = 20000000;
int T, k, b, x0, p;
int phi[N], prime[N], cnt;
void Euler(reg int n) // 线性筛 phi
{
phi[1] = 1;
for (reg int i = 2; i <= n; ++ i)
{
if (!phi[i]) prime[ ++ cnt] = i, phi[i] = i - 1;
for (reg int j = 1; j <= cnt; ++ j)
{
if (i * prime[j] > n) break;
if (i % prime[j] == 0) {phi[i * prime[j]] = phi[i] * prime[j]; break;}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
int qpow(reg int a, reg int b, reg int p) // 计算 a^b % mod p
{
reg int res = 1;
for (; b; b >>= 1)
{
if (b % 2 == 1) res = 1ll * res * a % p;
a = 1ll * a * a % p;
}
return res;
}
int sum(reg int a, reg int b, reg int p) // 计算 sum(a, b) % p
{
if (!b) return 1;
if (b & 1) return 1ll * (1 + qpow(a, b / 2 + 1, p)) * sum(a, b / 2, p) % p;
return (1 + 1ll * a * sum(a, b - 1, p)) % p;
}
LL work()
{
// 特判 k = 0, 1
if (k == 0) return (b == x0) ? 1 : -1;
if (k == 1)
{
if (!b) return -1;
return p / __gcd(p, b) + (b == x0);
}
reg int q = p / __gcd(1ll * p, 1ll * (k - 1) * x0 + b);
if (q == 1) return 1 + (x0 == b);
if (__gcd(k, q) > 1) return -1;
reg int ph = phi[q], res = ph; // res 即上文的 n
for (reg int i = 1; i * i <= ph; ++ i)
if (ph % i == 0)
{
if (qpow(k, i, q) == 1) {res = i; break;}
if (qpow(k, ph / i, q) == 1) res = ph / i;
}
reg int ans = sum(k, res - 1, q);
if (!ans) return res + (x0 == b);
return 1ll * q / __gcd(q, ans) * res + (x0 == b);
}
int main()
{
Euler(19260817);
scanf("%d", &T);
while (T -- )
{
scanf("%d %d %d %d", &k, &b, &x0, &p);
printf("%lld\n", work());
}
}