Submission #35152746


Source Code Expand

use proconio::input;

fn main() {
    input! {
        t: usize,
        case: [(usize, usize, usize, usize, usize); t]
    }

    for (p, a, b, s, g) in case {
        if a == 0 {
            if s == g {
                println!("0");
            } else if b == g {
                println!("1");
            } else {
                println!("-1");
            }
        } else {
            // f(x) = (ax+b) % p
            // fm(x) = (a^mx + a^(m-1)b + ... + b) % p = (a^mx + b * (a^m-1) /  (a-1)) % p
            let m = sqrt(p);
            let a_inv = mod_div(1, a, p);

            let f_inv = |y: usize| {
                (y + p - b) *  a_inv %  p
            };

            let result = if a == 1 {
                let f_m = |x: usize| {
                    (x + m * b) % p
                };
                baby_step_giant_step(s, g, p, m, f_m, f_inv)
            } else {
                let a_pow_m = mod_pow(a, m, p);
                let a_minus_1_inv = mod_div(1, a-1, p);
                let f_m = |x: usize| {
                    (a_pow_m * x + b * ((a_pow_m-1) * a_minus_1_inv % p)) % p
                };
                baby_step_giant_step(s, g, p, m, f_m, f_inv)
            };

            match result {
                Some(result) => {
                    println!("{}", result);
                }
                None => {
                    println!("-1");
                }
            }
        }
    }
}



/// xからyにするにはfを何回適用すればいいか求める
/// # Arguments
///
/// * `x` - 開始状態
/// * `y` - 終了状態
/// * `max` - `f`を適用する回数の上限
/// * `m`, `f_m` - `x`に`f`を`m`回適用したときの状態
/// * `f_inv` - `f`の逆関数
///
/// # Returns
/// * `Some(n)` - `f^n(x)=y`であるような最小の`n`
/// * `None` - 'max`回以下では`x`を`y`にできない
///
fn baby_step_giant_step<T, FM, FINV>(x: T, y: T, max: usize, m: usize, f_m: FM, f_inv: FINV) -> Option<usize>
where
	T: Copy + Eq + std::hash::Hash,
	FM: Fn(T) -> T,
	FINV: Fn(T) -> T
{
	let mut result = None;
	let mut candidates = std::collections::HashSet::new();
	let mut candidate = y;
	for _j in (0..m).rev() {
		candidates.insert(candidate);
		candidate = f_inv(candidate);
	}

	let mut f_im_x = x;
	for i in 0..=max/m {
		if candidates.contains(&f_im_x) {
			let mut v = f_m(f_im_x);
			for j in (0..m).rev() {
				v = f_inv(v);
				if v == y {
					result = Some(i * m + j);
				}
			}

            break;
		}

        f_im_x = f_m(f_im_x);
	}

	result
}


/// 平方根
///
/// # Arguments
///
/// * `n` - 対象の数
///
/// # Returns
///
/// n^0.5(小数点以下切り捨て)
///
pub fn sqrt(n: usize) -> usize {
	if n == 0 {
		0
	} else {
		b_search_find_last(1, n, |i| i <= n / i).unwrap()
	}
}

pub fn mod_pow(n: usize, mut k: usize, p: usize) -> usize {
    let mut val = n;
    let mut result: usize = 1;
    while k > 0 {
        if k % 2 == 1 {
            result = (result * val) % p;
        }
        val = (val * val) % p;
        k /= 2;
    }

    result
}

pub fn mod_div(a: usize, b: usize, p: usize) -> usize {
    a * mod_pow(b, p-2, p) % p
}

/// 二分探索
///
/// # Arguments
///
/// * `[left, right]`: 探索範囲
/// * `is_valid(i)` - iが有効かどうか
///
/// # Returns
/// * `Some(i)` - is_valid(i)がtrueになる最後のi
/// * `None` - is_valid(i)がすべてfalseになる場合
///
pub fn b_search_find_last<P>(left: usize, right: usize, is_valid: P) -> Option<usize> where
	P: Fn(usize) -> bool
{
	let mut min = left;
	let mut max = right;

	if min > max {
		None
	} else {
		while min < max {
			let c = max - (max - min) / 2;

			if is_valid(c) {
				min = c;
			} else {
				max = c - 1;
			}
		}

		if is_valid(min) {
			Some(min)
		} else {
			None
		}
	}
}

Submission Info

Submission Time
Task G - Sequence in mod P
User Hirekatsu
Language Rust (1.42.0)
Score 600
Code Size 3961 Byte
Status AC
Exec Time 214 ms
Memory 3128 KiB

Judge Result

Set Name Sample All
Score / Max Score 0 / 0 600 / 600
Status
AC × 1
AC × 27
Set Name Test Cases
Sample sample_01.txt
All random_01.txt, random_02.txt, random_03.txt, random_04.txt, random_05.txt, random_06.txt, random_07.txt, random_08.txt, random_09.txt, random_10.txt, random_11.txt, random_12.txt, random_13.txt, random_14.txt, random_15.txt, random_16.txt, random_17.txt, random_18.txt, random_19.txt, random_20.txt, random_21.txt, random_22.txt, random_23.txt, random_24.txt, random_25.txt, random_26.txt, sample_01.txt
Case Name Status Exec Time Memory
random_01.txt AC 6 ms 2124 KiB
random_02.txt AC 2 ms 2072 KiB
random_03.txt AC 2 ms 1984 KiB
random_04.txt AC 2 ms 2036 KiB
random_05.txt AC 209 ms 3004 KiB
random_06.txt AC 180 ms 2984 KiB
random_07.txt AC 191 ms 2968 KiB
random_08.txt AC 176 ms 2904 KiB
random_09.txt AC 184 ms 2964 KiB
random_10.txt AC 193 ms 3128 KiB
random_11.txt AC 195 ms 3024 KiB
random_12.txt AC 190 ms 2932 KiB
random_13.txt AC 214 ms 2956 KiB
random_14.txt AC 199 ms 2932 KiB
random_15.txt AC 110 ms 3104 KiB
random_16.txt AC 89 ms 2476 KiB
random_17.txt AC 187 ms 2932 KiB
random_18.txt AC 203 ms 3000 KiB
random_19.txt AC 197 ms 2908 KiB
random_20.txt AC 197 ms 3128 KiB
random_21.txt AC 207 ms 2932 KiB
random_22.txt AC 192 ms 2928 KiB
random_23.txt AC 209 ms 3036 KiB
random_24.txt AC 193 ms 2892 KiB
random_25.txt AC 201 ms 2940 KiB
random_26.txt AC 203 ms 2968 KiB
sample_01.txt AC 1 ms 2064 KiB