【Gym103261D】FFT Algorithm (Carmichael function + Pollard Rho)

题目链接

题目大意

给定 m , k ( m ≤ 1 0 18 , 15 ≤ k ≤ 23 ) m, k (m\le 10^{18}, 15\le k\le 23) m,k(m1018,15k23) ,求一个数 w w w 满足:

  • w 2 k ≡ 1 ( m o d m ) w^{2^k}\equiv 1\pmod m w2k1(modm)
  • w 2 k − 1 ≢ 1 ( m o d m ) w^{2^{k-1}}\not\equiv 1\pmod m w2k11(modm)

思路

Carmichael 函数 λ ( n ) = max ⁡ { o ( a ) ∣ a ⊥ n } \lambda(n)=\max\{o(a)\mid a\perp n\} λ(n)=max{o(a)an}

其中

  • λ ( 2 ) = 1 \lambda(2)=1 λ(2)=1
  • λ ( 4 ) = 2 \lambda(4)=2 λ(4)=2
  • λ ( 2 k ) = 2 k − 2 ( k ≥ 3 ) \lambda(2^k)=2^{k-2}(k\ge 3) λ(2k)=2k2(k3)
  • λ ( p k ) = p k − 1 ( p − 1 ) ( p ≥ 3 ) \lambda(p^k)=p^{k-1}(p-1)(p\ge 3) λ(pk)=pk1(p1)(p3)
  • λ ( u v ) = lcm ( λ ( u ) , λ ( v ) ) ( u ⊥ v ) \lambda(uv)=\text{lcm} (\lambda(u), \lambda(v))(u\perp v) λ(uv)=lcm(λ(u),λ(v))(uv)

原根存在的充要条件是 λ ( n ) = φ ( n ) \lambda(n)=\varphi(n) λ(n)=φ(n) ,即 n = 1 , 2 , 4 , p k , 2 p k ( p ≥ 3 ) n=1,2,4,p^k, 2p^k(p\ge 3) n=1,2,4,pk,2pk(p3)

答案存在当且仅当 2 k ∣ λ ( m ) 2^k|\lambda(m) 2kλ(m)

为了求 λ ( m ) \lambda(m) λ(m) ,需要对 m m m 进行质因数分解。可以采用 Pollard Rho 算法:

  1. 利用 Miller-Rabin 判断本身是否是质数
  2. 如果不是质数,随机一个序列 x , f ( x ) , f ( f ( x ) ) , . . . x,f(x),f(f(x)),... x,f(x),f(f(x)),... ,其中 f ( x ) = x 2 + c f(x)=x^2+c f(x)=x2+c (这样可以保证 x 1 − x 2 ∣ f ( x 1 ) − f ( x 2 ) x_1-x_2|f(x_1)-f(x_2) x1x2f(x1)f(x2)),序列最后会形成一个 ρ \rho ρ 形。我们可以搞两个变量 x 1 , x 2 x_1,x_2 x1,x2 ,初始都设成 x x x ,然后每次迭代时令 x 1 = f ( x 1 ) , x 2 = f ( f ( x 2 ) ) x_1=f(x_1), x_2=f(f(x_2)) x1=f(x1),x2=f(f(x2)) ,依次检验即可。如果遇到套圈的情况,就换个种子重新生成。(实际操作时,一般让 x = 2 , c = 1 x=2,c=1 x=2,c=1 ,127步为一组判断。如果套圈就令 c + + c++ c++ )期望复杂度 O ( n 1 4 ) O(n^{\frac{1}{4}}) O(n41)

求出来后,随机一些 x x x ,令 w = x λ ( m ) 2 k w=x^\frac{\lambda(m)}{2^k} w=x2kλ(m) ,多试一些就能出答案。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll prime[12] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
ll mul(ll x, ll y, ll mod) {  // can also use __int128 on Linux
    ll ret = 0;
    while (y) {
        if (y & 1) ret = (ret + x) % mod;
        x = x * 2 % mod;
        y >>= 1;
    }
    return ret;
}
ll pw(ll x, ll y, ll mod) {
    ll ret = 1;
    while (y) {
        if (y & 1) ret = mul(ret, x, mod);
        x = mul(x, x, mod);
        y >>= 1;
    }
    return ret;
}
bool isprime(ll x) {
    if (x == 1) return false;
    for (int i = 0; i < 12; ++i) {
        if (x == prime[i]) return true;  // special(itself)
        ll t = x - 1;
        if (pw(prime[i], t, x) != 1) return false;  // failure
        while (t % 2 == 0) {
            t >>= 1;
            ll now = pw(prime[i], t, x);
            if (now == x - 1) break;
            if (now != 1) return false;  // failure
        }
    }
    return true;
}
ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll lcm(ll x, ll y) { return x / gcd(x, y) * y; }
ll pollard_rho(ll x) {
    if (x % 2 == 0) return 2;  // special(unable to generate 2)
    ll x1 = 2, x2 = 2, c = 1;
    while (1) {
        ll y1 = x1, y2 = x2, y = 1;
        for (int i = 1; i <= 127; ++i) {
            y1 = (mul(y1, y1, x) + c) % x;
            y2 = (mul(y2, y2, x) + c) % x;
            y2 = (mul(y2, y2, x) + c) % x;
            y = mul(y, y2 - y1 + x,
                    x);  // to generate possible (times of factors)
        }
        y = gcd(y, x);
        if (y == x) {  // rejudge one by one
            for (int i = 1; i <= 127; ++i) {
                x1 = (mul(x1, x1, x) + c) % x;
                x2 = (mul(x2, x2, x) + c) % x;
                x2 = (mul(x2, x2, x) + c) % x;
                y = gcd(x2 - x1 + x, x);
                if (y == x)
                    ++c;
                else if (y > 1)
                    return y;
            }
        } else if (y > 1)
            return y;
        x1 = y1;
        x2 = y2;
    }
}
ll sta[1005], top = 0;
void decompose(ll x) {
    if (x == 1) return;
    if (isprime(x)) {
        sta[++top] = x;
        return;
    }
    ll t = pollard_rho(x);
    decompose(t);
    decompose(x / t);
}
ll m;
int k;
ll solve() {
    int cnt = 0;
    ll x = m;
    while (x % 2 == 0) {
        x >>= 1;
        ++cnt;
    }
    ll lambda;
    if (cnt <= 1)
        lambda = 1;
    else if (cnt == 2)
        lambda = 2;
    else
        lambda = (1ll << cnt - 2);
    top = 0;
    decompose(x);
    sort(sta + 1, sta + top + 1);
    for (int i = 1, last; i <= top; i = last + 1) {
        ll tmp = sta[i] - 1;
        last = i;
        while (last < top && sta[last + 1] == sta[i]) {
            ++last;
            tmp *= sta[i];
        }
        lambda = lcm(lambda, tmp);
    }
    // printf("%lld\n", lambda);
    if (lambda & ((1ll << k) - 1))
        return -1;
    else {
        for (ll x = 1; x < m; x++) {
            if (gcd(x, m) > 1) continue;
            ll w = pw(x, lambda >> k, m);
            if (pw(w, 1ll << k - 1, m) != 1) return w;
        }
    }
    return -2;
}
int main() {
    scanf("%lld%d", &m, &k);
    printf("%lld", solve());

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值