题解 【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\).

首先易知

\[((k−1)\times x_0+b)\times (1+k+k^2+k^3+...k^{n−1})\equiv 0 \bmod p \]

我们设 \(R = (k−1)\times x_0+b\), 那么就有

\[R\times (1+k+k^2+k^3+...k^{n−1})\equiv 0 \bmod p \]

这里我们可以把 \(\gcd(R, p)\) 处理掉,具体的:

\(q = \dfrac{p}{\gcd(R, p)}\).

  • \(q = 1\), 就有 \(r = 1 + [x_0 = b]\).(注:\([x]\) 表示若 \(x\) 为真,则为 \(1\); 否则为 \(0\)
  • \(q \ge 2\),就有

\[1+k+k^2+k^3+...k^{n−1}\equiv 0\bmod q \]

那么肯定有

\[k^n-1\equiv 0 \bmod q \]

即:\(n\)\(k\) 关于\(\bmod q\) 的阶。

又有

\[k^{φ(q)}\equiv 1\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\).

考虑分治法:

\[\begin{cases}sum(p,c)=(1+p^\frac{c+1}2)\times sum(p,\frac{c+1}2)&c\bmod2=1\\sum(p,c)=(1+p^{\frac{c}2+1})\times sum(p,\frac{c}2)& c\bmod2=0\end{cases} \]

\(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());
    }
}
posted @ 2021-11-25 12:28  DreamsChaser  阅读(87)  评论(0)    收藏  举报
Live2D