#[allow(unused_imports)]
use std::cmp::{max, min, Ordering};
#[allow(unused_imports)]
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
#[allow(unused_imports)]
use std::io::{stdin, stdout, BufWriter, StdoutLock, Write};
#[allow(unused_imports)]
use std::iter::FromIterator;
#[allow(unused_macros)]
macro_rules ! input { ( source = $ s : expr , $ ( $ r : tt ) * ) => { let mut iter = $ s . split_whitespace ( ) ; let mut next = || { iter . next ( ) . unwrap ( ) } ; input_inner ! { next , $ ( $ r ) * } } ; ( $ ( $ r : tt ) * ) => { let stdin = std :: io :: stdin ( ) ; let mut bytes = std :: io :: Read :: bytes ( std :: io :: BufReader :: new ( stdin . lock ( ) ) ) ; let mut next = move || -> String { bytes . by_ref ( ) . map ( | r | r . unwrap ( ) as char ) . skip_while ( | c | c . is_whitespace ( ) ) . take_while ( | c |! c . is_whitespace ( ) ) . collect ( ) } ; input_inner ! { next , $ ( $ r ) * } } ; }
#[allow(unused_macros)]
macro_rules ! input_inner { ( $ next : expr ) => { } ; ( $ next : expr , ) => { } ; ( $ next : expr , $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let $ var = read_value ! ( $ next , $ t ) ; input_inner ! { $ next $ ( $ r ) * } } ; }
#[allow(unused_macros)]
macro_rules ! read_value { ( $ next : expr , ( $ ( $ t : tt ) ,* ) ) => { ( $ ( read_value ! ( $ next , $ t ) ) ,* ) } ; ( $ next : expr , [ $ t : tt ; $ len : expr ] ) => { ( 0 ..$ len ) . map ( | _ | read_value ! ( $ next , $ t ) ) . collect ::< Vec < _ >> ( ) } ; ( $ next : expr , chars ) => { read_value ! ( $ next , String ) . chars ( ) . collect ::< Vec < char >> ( ) } ; ( $ next : expr , bytes ) => { read_value ! ( $ next , String ) . into_bytes ( ) } ; ( $ next : expr , usize1 ) => { read_value ! ( $ next , usize ) - 1 } ; ( $ next : expr , $ t : ty ) => { $ next ( ) . parse ::<$ t > ( ) . expect ( "Parse error" ) } ; }
#[allow(dead_code)]
pub fn with_bufwriter<F: FnOnce(BufWriter<StdoutLock>) -> ()>(f: F) {
    let out = stdout();
    let writer = BufWriter::new(out.lock());
    f(writer)
}

#[allow(dead_code)]
pub fn divisor(n: u64) -> Vec<u64> {
    let mut div: Vec<u64> = Vec::with_capacity(n as usize);
    let mut i = 1;
    while i * i <= n {
        if n % i == 0 {
            div.push(i);
            if i * i != n {
                div.push(n / i);
            }
        }
        i += 1;
    }
    div.sort();
    div
}

#[allow(dead_code)]
pub fn prime_factor(n: u64) -> HashMap<u64, u64> {
    let mut prime_factors: HashMap<u64, u64> = HashMap::with_capacity(n as usize);
    let mut n = n;
    let mut i = 2;
    while i * i <= n {
        while n % i == 0 {
            if prime_factors.contains_key(&i) {
                let tmp = prime_factors[&i];
                prime_factors.insert(i, tmp + 1);
            } else {
                prime_factors.insert(i, 1);
            }
            n /= i;
        }
        i += 1;
    }
    if n != 1 {
        prime_factors.insert(n, 1);
    }
    prime_factors
}



#[allow(unused_macros)]
macro_rules ! debug { ( $ ( $ a : expr ) ,* ) => { eprintln ! ( concat ! ( $ ( stringify ! ( $ a ) , " = {:?}, " ) ,* ) , $ ( $ a ) ,* ) } ; }
#[allow(dead_code)]
fn main() {
    input!{
        n: u64,
        p: u64,
    }
    if n == 1 {
        println!("{}", p);
        return;
    }
    let mut ans = 1;
    let mut tmp = p;
    let mut i = 2;
    while i * i <= p {
        let mut count = 0;
        while tmp % i == 0 {
            count += 1;
            tmp /= i;
        }
        if count >= n {
            for _j in 0..count/n {
                ans *= i;
            }
        }
        i += 1;
    }
    println!("{}", ans);
}

Measured memory_bytes: 4352