Problem:
LCM Tour
問題
Osaka 王国には、$1$ から $N$ までの整数の番号がつけられた $N$ 個の都市があります。Daigaku 君は、都市 $1$ を出発して都市 $N$ で終えるツアーを計画しています。
Osaka 王国には $M$ 本の一方通行の道があり、それぞれの道には
魅力度
を表す正の整数が付けられています。$i$ 番目 ($1 \leq i \leq M$) の道の出発点は都市 $a_i$ 、到着点は都市 $b_i$ で、その魅力度は $c_i$ です。$i$ 番目の道を通って都市 $a_i$ から都市 $b_i$ まで移動することができますが、$a_i \neq b_i$ のときは $i$ 番目の道を通って $b_i$ から $a_i$ へ逆向きに移動することはできません。なお、同じ出発点と到着点の組に対して複数の道が存在したり、出発点と到着点が同一の都市である道が存在したりする場合があります。
ツアーの道順は $1$ 本以上の道からなり、以下の条件を満たします。
最初の道の出発点は都市 $1$ である
最後の道の到着点は都市 $N$ である
到着点が $x$ の道の次に道を通行する場合、その出発点は $x$ である
同じ都市を何回訪れてもよく、同じ道を何回通行しても良い
都市 $N$ に到着した後も、ツアーを終了することなく都市 $N$ を出発点としている道を通行しても良い
道順が上の条件を満たすツアーの魅力度は、ツアーの中で通行した道の魅力度の最小公倍数です。
条件を満たすようなツアーのうち、魅力度が最小になるものの魅力度と最大になるものの魅力度を求めてください。ただし条件を満たすようなツアーが存在しないとき、代わりに
IMPOSSIBLE
と出力してください。
制約
$2 \leq N \leq 1000$
$0 \leq M \leq 2000$
$1 \leq a_i \leq N$
$1 \leq b_i \leq N$
$1 \leq c_i \leq 20$
入力はすべて整数
入力
入力は以下の形式で標準入力から与えられます。
$N$ $M$
$a_1$ $b_1$ $c_1$
$a_2$ $b_2$ $c_2$
$\vdots$
$a_M$ $b_M$ $c_M$
出力
条件を満たすようなツアーが存在するとき、魅力度の最小値を $A$ 、最大値を $B$ として以下の形式で出力してください。
A B
条件を満たすようなツアーが存在しないとき、
IMPOSSIBLE
と1行に出力してください。
入力例 1
3 3
1 2 4
1 3 7
2 3 6
出力例 1
7 12
最小値を満たす道順は、道 $2$ のみを通行するときで、このときの魅力度は $7$ です。
最大値を満たす道順は、道 $1$、道 $3$をこの順に通行するときです。このとき、魅力度は $4$ と $6$ の最小公倍数、つまり $12$ です。
入力例 2
3 0
出力例 2
IMPOSSIBLE
入力例 3
5 7
1 2 2
1 3 3
2 3 6
3 4 2
4 4 7
4 5 10
5 3 11
出力例 3
30 2310

Submitted solutions:

code_snippet: // AOJ #3269
// LCM Tour 2025.2.5

#include <bits/stdc++.h>
using namespace std;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0, c = gc();
	if (c == '-') {	c = gc();
		do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
		return -n;
	}
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) { // 整数の表示（出力）
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

// lcmの計算関数（std::gcdを利用）
int lcm_func(int a, int b) {
    int g = gcd(a, b);
    return a / g * b;
}

int main() {
    int N = Cin(), M = Cin();

    vector<vector<pair<int,int>>> graph(N+1);
    for(int i = 0; i < M; i++){
        int a = Cin(), b = Cin(), c = Cin();
        graph[a].push_back({b, c});
    }
    
    vector<unordered_set<int>> visited(N+1);
    queue<pair<int,int>> q;
    
    visited[1].insert(1);
    q.push({1, 1});
    
    while(!q.empty()){
        auto [node, curLCM] = q.front();
        q.pop();
        
        for(auto &edge : graph[node]){
            int nxt = edge.first;
            int weight = edge.second;
            int newLCM = lcm_func(curLCM, weight);
            if(visited[nxt].find(newLCM) == visited[nxt].end()){
                visited[nxt].insert(newLCM);
                q.push({nxt, newLCM});
            }
        }
    }
    
    if(visited[N].empty()) cout << "IMPOSSIBLE" << endl;
    else {
        int minVal = 1e9, maxVal = 0;
        for(auto val : visited[N]){
            if(val < minVal) minVal = val;
            if(val > maxVal) maxVal = val;
        }
        cout << minVal << " " << maxVal << endl;
    }
    return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 10764
final_rank: 2

code_snippet: #include "bits/stdc++.h"


using namespace std;

using i64 = long long;

constexpr i64 mod = 998244353;


int main(){
    int n, m;
    cin >> n >> m;
    vector<i64> a(m), b(m), c(m);
    vector<vector<pair<int,int>>> edges(n);
    for(int i = 0; i < m; ++i){
        cin >> a[i] >> b[i] >> c[i];
        --a[i], --b[i];
        edges[a[i]].emplace_back(b[i], c[i]);
    }
    set<i64> s;
    for(int i = 0; i < (1 << 20); ++i){
        i64 lc = 1;
        for(int j = 0; j < 20; ++j){
            if(i & (1 << j)){
                lc = lc * (j + 1) / gcd(lc, j + 1);
            }
        }
        s.emplace(lc);
    }
    vector<i64> lcms(s.begin(), s.end());
    unordered_map<i64,int> lcmap;
    for(int i = 0; i < lcms.size(); ++i){
        lcmap[lcms[i]] = i;
    }
    i64 t = lcmap.size();
    vector<vector<i64>> dp(n, vector<i64>(t, 0));
    dp[0][0] = 1;
    for(int lc = 0; lc < t; ++lc){

        function<void(int)> f = [&](int x){
            for(auto [y, cos] : edges[x]){
                i64 ni = lcmap[lcms[lc] * cos / gcd(lcms[lc], cos)];
                if(dp[y][ni]){
                    continue;
                }
                dp[y][ni] = true;
                if(lc == ni){
                    f(y);
                }
            }
        };

        for(int i = 0; i < n; ++i){
            if(dp[i][lc]){
                f(i);
            }
        }
    }
    i64 mi = 1e9;
    i64 ans = -1;

    for(int j = 0; j < t; ++j){
        if(dp[n - 1][j]){
            if(mi == 1e9){
                mi = lcms[j];
            }
            ans = lcms[j];
        }
    }
    if(ans == -1){
        cout << "IMPOSSIBLE" << endl;
        return 0;
    }
    cout << mi << " " << ans << endl;
}
accuracy: 1
time_ms: 360
memory_kb: 10764
final_rank: 12

code_snippet: #include <bits/stdc++.h>
using namespace std;

long long gcd2(long long a,long long b) {
    if(a == 0 || b == 0) {
        return max(a,b);
    }
    if(a % b == 0) {
        return b;
    }
    else {
        return gcd2(b,a % b);
    }
}

int main() {
    vector<int>a;
    for(int i = 1; i < (1 << 20); i++) {
        int b = 0;
        for(int j = 0; j < 20; j++) {
            if(1 & (i >> j)) {
                if(b == 0) {
                    b = j+1;
                }
                b = 1ll*b/gcd2(b,j+1)*(j+1);
            }
        }
        a.push_back(b);
    }
    sort(a.begin(),a.end());
    a.erase(unique(a.begin(),a.end()),a.end());
    int N,M;
    cin >> N >> M;
    vector<vector<pair<int,int>>>road(N);
    for(int i = 0; i < M; i++) {
        int A,b,c;
        cin >> A >> b >> c;
        A--;
        b--;
        road[A].push_back({b,c});
    }
    vector<vector<bool>>used(N,vector<bool>(a.size()));
    used[0][0] = true;
    queue<pair<int,int>>que;
    que.push({0,0});
    while (!que.empty()) {
        pair<int,int>x = que.front();
        que.pop();
        for(pair<int,int>b:road[x.first]) {
            int nxt = b.first;
            int nxt2 = 1ll*a[x.second]/gcd2(a[x.second],b.second)*b.second;
            nxt2 = lower_bound(a.begin(),a.end(),nxt2)-a.begin();
            if(!used[nxt][nxt2]) {
                used[nxt][nxt2] = true;
                que.push({nxt,nxt2});
            }
        }
    }
    int ans1 = 1001001001,ans2 = -1;
    for(int i = 0; i < a.size(); i++) {
        if(used[N-1][i]) {
            ans1 = min(ans1,a[i]);
            ans2 = max(ans2,a[i]);
        }
    }
    if(ans2 == -1) {
        cout << "IMPOSSIBLE" << endl;
        return 0;
    }
    cout << ans1 << " " << ans2 << endl;
}
accuracy: 1
time_ms: 400
memory_kb: 9152
final_rank: 13

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define chmin(x,y) x = min(x,y)
#define chmax(x,y) x = max(x,y)
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001



#define SIZE 1005

struct Edge{
	Edge(int arg_to,ll arg_value){
		to = arg_to;
		value = arg_value;
	}
	int to;
	ll value;
};

int N,M;
map<ll,bool> visited[SIZE];
vector<Edge> G[SIZE];


//最大公約数
ll gcd(ll x,ll y){

	x = abs(x);
	y = abs(y);

	if(x < y){
		swap(x,y);
	}
	if(y == 0){
		return x;
	}else{
		return gcd(y,x%y);
	}
}

//最小公倍数
ll lcm(ll x,ll y){

	return (x*y)/gcd(x,y);
}

void dfs(int node,ll LCM){

	if(visited[node][LCM]){

		return;
	}

	visited[node][LCM] = true;

	for(int i = 0; i < G[node].size(); i++){

		int next = G[node][i].to;
		ll next_LCM = lcm(LCM,G[node][i].value);

		dfs(next,next_LCM);
	}
}


int main(){

	scanf("%d %d",&N,&M);

	int a,b,c;

	for(int i = 0; i < M; i++){

		scanf("%d %d %d",&a,&b,&c);
		a--;
		b--;
		G[a].push_back(Edge(b,c));
	}

	dfs(0,1);

	if(visited[N-1].size() == 0){

		printf("IMPOSSIBLE\n");
		return 0;
	}

	ll mini = HUGE_NUM,maxi = -HUGE_NUM;
	for(auto at = visited[N-1].begin(); at != visited[N-1].end(); at++){

		chmin(mini,at->first);
		chmax(maxi,at->first);
	}

	printf("%lld %lld\n",mini,maxi);

	return 0;
}
accuracy: 1
time_ms: 80
memory_kb: 14384
final_rank: 6

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define chmin(x,y) x = min(x,y)
#define chmax(x,y) x = max(x,y)
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001



#define SIZE 960

struct Edge{
	Edge(int arg_to,ll arg_value){
		to = arg_to;
		value = arg_value;
	}
	int to;
	ll value;
};

int N,M;
const ll BASE = 232792560;
map<ll,ll> MAP,REV;
bool visited[1005][SIZE];
vector<Edge> G[1005];


//最大公約数
ll gcd(ll x,ll y){

	x = abs(x);
	y = abs(y);

	if(x < y){
		swap(x,y);
	}
	if(y == 0){
		return x;
	}else{
		return gcd(y,x%y);
	}
}

//最小公倍数
ll lcm(ll x,ll y){

	return (x*y)/gcd(x,y);
}

void dfs(int node,ll LCM){

	if(visited[node][MAP[LCM]]){

		return;
	}
	if(node != 0 || LCM != 1){

		visited[node][MAP[LCM]] = true;
	}

	for(int i = 0; i < G[node].size(); i++){

		int next = G[node][i].to;
		ll next_LCM = lcm(LCM,G[node][i].value);

		dfs(next,next_LCM);
	}
}


int main(){

	vector<ll> vec;
	for(ll i=2; i*i <= BASE; i++){
		if(BASE%i != 0)continue;

		vec.push_back(i);

		if(i != BASE/i){

			vec.push_back(BASE/i);
		}
	}
	sort(vec.begin(),vec.end());
	for(int i = 0; i < vec.size(); i++){

		MAP[vec[i]] = i;
		REV[i] = vec[i];
	}

	scanf("%d %d",&N,&M);

	int a,b,c;

	for(int i = 0; i < M; i++){

		scanf("%d %d %d",&a,&b,&c);
		a--;
		b--;
		G[a].push_back(Edge(b,c));
	}

	dfs(0,1);

	ll mini = HUGE_NUM,maxi = -HUGE_NUM;

	for(int i = 0; i < vec.size(); i++){

		if(visited[N-1][i]){

			chmin(mini,REV[i]);
			chmax(maxi,REV[i]);
		}
	}
	if(mini == HUGE_NUM){

		printf("IMPOSSIBLE\n");
	}else{

		printf("%lld %lld\n",mini,maxi);
	}

	return 0;
}
accuracy: 0.3170731707317073
time_ms: 30
memory_kb: 3776
final_rank: 18

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define chmin(x,y) x = min(x,y)
#define chmax(x,y) x = max(x,y)
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001



#define SIZE 960

struct Edge{
	Edge(int arg_to,ll arg_value){
		to = arg_to;
		value = arg_value;
	}
	int to;
	ll value;
};

int N,M;
const ll BASE = 232792560;
map<ll,ll> MAP,REV;
bool visited[1005][SIZE];
vector<Edge> G[1005];


//最大公約数
ll gcd(ll x,ll y){

	x = abs(x);
	y = abs(y);

	if(x < y){
		swap(x,y);
	}
	if(y == 0){
		return x;
	}else{
		return gcd(y,x%y);
	}
}

//最小公倍数
ll lcm(ll x,ll y){

	return (x*y)/gcd(x,y);
}

void dfs(int node,ll LCM){

	if(visited[node][MAP[LCM]]){

		return;
	}
	visited[node][MAP[LCM]] = true;

	for(int i = 0; i < G[node].size(); i++){

		int next = G[node][i].to;
		ll next_LCM = lcm(LCM,G[node][i].value);

		dfs(next,next_LCM);
	}
}


int main(){

	vector<ll> vec;
	for(ll i=2; i*i <= BASE; i++){
		if(BASE%i != 0)continue;

		vec.push_back(i);

		if(i != BASE/i){

			vec.push_back(BASE/i);
		}
	}
	sort(vec.begin(),vec.end());
	for(int i = 0; i < vec.size(); i++){

		MAP[vec[i]] = i;
		REV[i] = vec[i];
	}

	scanf("%d %d",&N,&M);

	int a,b,c;

	for(int i = 0; i < M; i++){

		scanf("%d %d %d",&a,&b,&c);
		a--;
		b--;
		G[a].push_back(Edge(b,c));
	}

	dfs(0,1);

	ll mini = HUGE_NUM,maxi = -HUGE_NUM;

	for(int i = 0; i < vec.size(); i++){

		if(visited[N-1][i]){

			chmin(mini,REV[i]);
			chmax(maxi,REV[i]);
		}
	}
	if(mini == HUGE_NUM){

		printf("IMPOSSIBLE\n");
	}else{

		printf("%lld %lld\n",mini,maxi);
	}

	return 0;
}
accuracy: 0.3170731707317073
time_ms: 30
memory_kb: 3824
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < n; i++)
#define rep2(i, x, n) for (int i = x; i <= n; i++)
#define rep3(i, x, n) for (int i = x; i >= n; i--)
#define each(e, v) for (auto &e : v)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;

template <typename T>
bool chmax(T &x, const T &y) {
    return (x < y) ? (x = y, true) : false;
}

template <typename T>
bool chmin(T &x, const T &y) {
    return (x > y) ? (x = y, true) : false;
}

template <typename T>
int flg(T x, int i) {
    return (x >> i) & 1;
}

template <typename T>
void print(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
    if (v.empty()) cout << '\n';
}

template <typename T>
void printn(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << '\n';
}

template <typename T>
int lb(const vector<T> &v, T x) {
    return lower_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
int ub(const vector<T> &v, T x) {
    return upper_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
void rearrange(vector<T> &v) {
    sort(begin(v), end(v));
    v.erase(unique(begin(v), end(v)), end(v));
}

template <typename T>
vector<int> id_sort(const vector<T> &v, bool greater = false) {
    int n = v.size();
    vector<int> ret(n);
    iota(begin(ret), end(ret), 0);
    sort(begin(ret), end(ret), [&](int i, int j) { return greater ? v[i] > v[j] : v[i] < v[j]; });
    return ret;
}

template <typename S, typename T>
pair<S, T> operator+(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first + q.first, p.second + q.second);
}

template <typename S, typename T>
pair<S, T> operator-(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first - q.first, p.second - q.second);
}

template <typename S, typename T>
istream &operator>>(istream &is, pair<S, T> &p) {
    S a;
    T b;
    is >> a >> b;
    p = make_pair(a, b);
    return is;
}

template <typename S, typename T>
ostream &operator<<(ostream &os, const pair<S, T> &p) {
    return os << p.first << ' ' << p.second;
}

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;
const int MOD = 1000000007;
// const int MOD = 998244353;

template <typename T, bool directed = false>
struct Weighted_Graph {
    struct edge {
        int to;
        T cost;
        int id;
        edge(int to, T cost, int id) : to(to), cost(cost), id(id) {}
    };

    vector<vector<edge>> es;
    const T INF_T = numeric_limits<T>::max() / 2;
    const int n;
    int m;

    Weighted_Graph(int n) : es(n), n(n), m(0) {}

    void add_edge(int from, int to, T cost) {
        es[from].emplace_back(to, cost, m);
        if (!directed) es[to].emplace_back(from, cost, m);
        m++;
    }

    set<pil> used;
    ll ma = -INF, mi = INF;

    void dfs(int now, ll c) {
        if (used.count(pil(now, c))) return;
        used.emplace(now, c);
        if (now == n - 1) chmin(mi, c), chmax(ma, c);
        each(e, es[now]) {
            ll nc = lcm(c, e.cost);
            dfs(e.to, nc);
        }
    }

    void solve() {
        dfs(0, 1);
        if (ma == -INF) {
            cout << "IMPOSSIBLE\n";
        } else {
            cout << mi << ' ' << ma << '\n';
        }
    }
};

int main() {
    int N, M;
    cin >> N >> M;

    Weighted_Graph<ll, true> G(N);
    rep(i, M) {
        int u, v, c;
        cin >> u >> v >> c;
        u--, v--;
        G.add_edge(u, v, c);
    }

    G.solve();
}
accuracy: 1
time_ms: 110
memory_kb: 14124
final_rank: 9

code_snippet: #pragma region Macros
#include <bits/stdc++.h>
using namespace std;
template <class T> inline bool chmax(T &a, T b) {
    if(a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <class T> inline bool chmin(T &a, T b) {
    if(a > b) {
        a = b;
        return 1;
    }
    return 0;
}
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

struct Setup {
    Setup() {
        cin.tie(0);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(15);
    }
} __Setup;

using ll = long long;
#define OVERLOAD3(_1, _2, _3, name, ...) name
#define ALL(v) (v).begin(), (v).end()
#define RALL(v) (v).rbegin(), (v).rend()
#define REP1(i, n) for(int i = 0; i < int(n); i++)
#define REP2(i, a, b) for(int i = (a); i < int(b); i++)
#define REP(...) OVERLOAD3(__VA_ARGS__, REP2, REP1)(__VA_ARGS__)
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) ((int)(v).size())
const int INF = 1 << 30;
const ll LLINF = 1LL << 60;
constexpr int MOD = 1000000007;
constexpr int MOD2 = 998244353;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};

void Case(int i) { cout << "Case #" << i << ": "; }
int popcount(int x) { return __builtin_popcount(x); }
ll popcount(ll x) { return __builtin_popcountll(x); }
#pragma endregion Macros

int main() {
    int N, M;
    cin >> N >> M;
    vector<vector<pair<int, ll>>> G(N);
    REP(i, M) {
        int a, b, c;
        cin >> a >> b >> c;
        a--, b--;
        G[a].emplace_back(b, c);
        // G[b].emplace_back(a, c);
    }

    set<pair<ll, int>> seen;
    ll mx = -LLINF, mn = LLINF;
    auto dfs = [&](auto&& self, ll l, int u) -> void {
        if(seen.count({l, u})) return;
        seen.emplace(l, u);
        if(u == N-1) {
            chmax(mx, l);
            chmin(mn, l);
        }
        for(auto [v, c] : G[u]) {
            self(self, lcm(l, c), v);
        }
    };
    dfs(dfs, 1, 0);
    
    if(mx == -LLINF) {
        cout << "IMPOSSIBLE\n";
    } else {
        cout << mn << ' ' << mx << endl;
    }
}
accuracy: 1
time_ms: 80
memory_kb: 14340
final_rank: 5

code_snippet: #pragma GCC optimize("Ofast")
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdio>
#include <ctime>
#include <assert.h>
#include <chrono>
#include <random>
#include <numeric>
#include <set>
#include <deque>
#include <stack>
#include <sstream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <array>
#include <bitset>
using namespace std;
typedef long long int ll;
typedef unsigned long long ull;

mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ll myRand(ll B) {
    return (ull)rng() % B;
}
inline ll time() {
    return static_cast<long double>(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count()) * 1e-9;
}

int main(){
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    constexpr int inf = 1e9;
    int mi = inf, mx = -inf;
    int n,m; cin >> n >> m;
    vector<vector<pair<int,int>>> g(n);
    for(int i=0;i<m;i++){
        int x,y; cin >> x >> y;
        x--; y--;
        int c; cin >> c;
        g[x].push_back({y,c});
    }
    set<pair<int,int>> st;
    auto dfs=[&](auto dfs,int s,int L)->void{
        if(s == n-1){
            mi = min(mi, L);
            mx = max(mx, L);
        }
        if(st.find({s,L}) != st.end()){
            return;
        }
        st.insert({s,L});
        for(auto p:g[s]){
            int t = p.first;
            int nL = lcm(L,p.second);
            dfs(dfs,t,nL);
        }
    };
    dfs(dfs,0,1);
    if(mi == inf){
        cout << "IMPOSSIBLE" << endl;
    }
    else{
        cout << mi << " " << mx << endl;
    }
}
accuracy: 1
time_ms: 100
memory_kb: 11404
final_rank: 7

code_snippet: #include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <chrono>
#include <cmath>
#include <complex>
#include <deque>
#include <forward_list>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
using lint = long long;
using pint = pair<int, int>;
using plint = pair<lint, lint>;
struct fast_ios { fast_ios(){ cin.tie(nullptr), ios::sync_with_stdio(false), cout << fixed << setprecision(20); }; } fast_ios_;
#define ALL(x) (x).begin(), (x).end()
#define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i<i##_end_;i++)
#define IFOR(i, begin, end) for(int i=(end)-1,i##_begin_=(begin);i>=i##_begin_;i--)
#define REP(i, n) FOR(i,0,n)
#define IREP(i, n) IFOR(i,0,n)
template <typename T, typename V>
void ndarray(vector<T>& vec, const V& val, int len) { vec.assign(len, val); }
template <typename T, typename V, typename... Args> void ndarray(vector<T>& vec, const V& val, int len, Args... args) { vec.resize(len), for_each(begin(vec), end(vec), [&](T& v) { ndarray(v, val, args...); }); }
template <typename T> bool chmax(T &m, const T q) { return m < q ? (m = q, true) : false; }
template <typename T> bool chmin(T &m, const T q) { return m > q ? (m = q, true) : false; }
int floor_lg(long long x) { return x <= 0 ? -1 : 63 - __builtin_clzll(x); }
template <typename T1, typename T2> pair<T1, T2> operator+(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first + r.first, l.second + r.second); }
template <typename T1, typename T2> pair<T1, T2> operator-(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first - r.first, l.second - r.second); }
template <typename T> vector<T> sort_unique(vector<T> vec) { sort(vec.begin(), vec.end()), vec.erase(unique(vec.begin(), vec.end()), vec.end()); return vec; }
template <typename T> int arglb(const std::vector<T> &v, const T &x) { return std::distance(v.begin(), std::lower_bound(v.begin(), v.end(), x)); }
template <typename T> int argub(const std::vector<T> &v, const T &x) { return std::distance(v.begin(), std::upper_bound(v.begin(), v.end(), x)); }
template <typename T> istream &operator>>(istream &is, vector<T> &vec) { for (auto &v : vec) is >> v; return is; }
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) { os << '['; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <typename T, size_t sz> ostream &operator<<(ostream &os, const array<T, sz> &arr) { os << '['; for (auto v : arr) os << v << ','; os << ']'; return os; }
#if __cplusplus >= 201703L
template <typename... T> istream &operator>>(istream &is, tuple<T...> &tpl) { std::apply([&is](auto &&... args) { ((is >> args), ...);}, tpl); return is; }
template <typename... T> ostream &operator<<(ostream &os, const tuple<T...> &tpl) { os << '('; std::apply([&os](auto &&... args) { ((os << args << ','), ...);}, tpl); return os << ')'; }
#endif
template <typename T> ostream &operator<<(ostream &os, const deque<T> &vec) { os << "deq["; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <typename T> ostream &operator<<(ostream &os, const set<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T, typename TH> ostream &operator<<(ostream &os, const unordered_set<T, TH> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T> ostream &operator<<(ostream &os, const multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T> ostream &operator<<(ostream &os, const unordered_multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T1, typename T2> ostream &operator<<(ostream &os, const pair<T1, T2> &pa) { os << '(' << pa.first << ',' << pa.second << ')'; return os; }
template <typename TK, typename TV> ostream &operator<<(ostream &os, const map<TK, TV> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
template <typename TK, typename TV, typename TH> ostream &operator<<(ostream &os, const unordered_map<TK, TV, TH> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
#ifdef HITONANODE_LOCAL
const string COLOR_RESET = "\033[0m", BRIGHT_GREEN = "\033[1;32m", BRIGHT_RED = "\033[1;31m", BRIGHT_CYAN = "\033[1;36m", NORMAL_CROSSED = "\033[0;9;37m", RED_BACKGROUND = "\033[1;41m", NORMAL_FAINT = "\033[0;2m";
#define dbg(x) cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << endl
#define dbgif(cond, x) ((cond) ? cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << endl : cerr)
#else
#define dbg(x) 0
#define dbgif(cond, x) 0
#endif

int main() {
    int N, M;
    cin >> N >> M;
    vector<vector<pint>> to(N);
    while (M--) {
        int a, b, c;
        cin >> a >> b >> c;
        --a, --b;
        to[a].emplace_back(b, c);
    }

    vector<int> vs(1 << 20, 1);
    FOR(S, 1, 1 << 20) {
        int i = floor_lg(S);
        vs[S] = lint(vs[S - (1 << i)]) * (i + 1) / __gcd<lint>(vs[S - (1 << i)], i + 1);
    }
    vs = sort_unique(vs);
    dbg(vs.size());
    dbg(vs.back());

    constexpr int BS = 960;
    vector<bitset<BS>> reach(N);

    vector Next(BS, vector<int>(21));
    REP(i, BS) {
        lint now = vs[i];
        FOR(e, 1, 21) {
            int nxt = arglb(vs, int(now * e / __gcd<lint>(now, e)));
            Next[i][e] = nxt;
        }
    }

    vector<pint> st;
    reach[0].set(0);
    st.emplace_back(0, 0);
    while (st.size()) {
        auto [now, d] = st.back();
        st.pop_back();
        for (auto [nxt, e] : to[now]) {
            if (!reach[nxt][Next[d][e]]) {
                reach[nxt][Next[d][e]] = 1;
                st.emplace_back(nxt, Next[d][e]);
            }
        }
    }
    if (reach.back().none()) {
        puts("IMPOSSIBLE");
    } else {
        int i = reach.back()._Find_first();
        int j = BS - 1;
        while (!reach.back()[j]) --j;
        cout << vs[i] << ' ' << vs[j] << '\n';
    }
}
accuracy: 1
time_ms: 40
memory_kb: 11416
final_rank: 3

code_snippet: #include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <chrono>
#include <cmath>
#include <complex>
#include <deque>
#include <forward_list>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
using lint = long long;
using pint = pair<int, int>;
using plint = pair<lint, lint>;
struct fast_ios { fast_ios(){ cin.tie(nullptr), ios::sync_with_stdio(false), cout << fixed << setprecision(20); }; } fast_ios_;
#define ALL(x) (x).begin(), (x).end()
#define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i<i##_end_;i++)
#define IFOR(i, begin, end) for(int i=(end)-1,i##_begin_=(begin);i>=i##_begin_;i--)
#define REP(i, n) FOR(i,0,n)
#define IREP(i, n) IFOR(i,0,n)
template <typename T, typename V>
void ndarray(vector<T>& vec, const V& val, int len) { vec.assign(len, val); }
template <typename T, typename V, typename... Args> void ndarray(vector<T>& vec, const V& val, int len, Args... args) { vec.resize(len), for_each(begin(vec), end(vec), [&](T& v) { ndarray(v, val, args...); }); }
template <typename T> bool chmax(T &m, const T q) { return m < q ? (m = q, true) : false; }
template <typename T> bool chmin(T &m, const T q) { return m > q ? (m = q, true) : false; }
int floor_lg(long long x) { return x <= 0 ? -1 : 63 - __builtin_clzll(x); }
template <typename T1, typename T2> pair<T1, T2> operator+(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first + r.first, l.second + r.second); }
template <typename T1, typename T2> pair<T1, T2> operator-(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first - r.first, l.second - r.second); }
template <typename T> vector<T> sort_unique(vector<T> vec) { sort(vec.begin(), vec.end()), vec.erase(unique(vec.begin(), vec.end()), vec.end()); return vec; }
template <typename T> int arglb(const std::vector<T> &v, const T &x) { return std::distance(v.begin(), std::lower_bound(v.begin(), v.end(), x)); }
template <typename T> int argub(const std::vector<T> &v, const T &x) { return std::distance(v.begin(), std::upper_bound(v.begin(), v.end(), x)); }
template <typename T> istream &operator>>(istream &is, vector<T> &vec) { for (auto &v : vec) is >> v; return is; }
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) { os << '['; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <typename T, size_t sz> ostream &operator<<(ostream &os, const array<T, sz> &arr) { os << '['; for (auto v : arr) os << v << ','; os << ']'; return os; }
#if __cplusplus >= 201703L
template <typename... T> istream &operator>>(istream &is, tuple<T...> &tpl) { std::apply([&is](auto &&... args) { ((is >> args), ...);}, tpl); return is; }
template <typename... T> ostream &operator<<(ostream &os, const tuple<T...> &tpl) { os << '('; std::apply([&os](auto &&... args) { ((os << args << ','), ...);}, tpl); return os << ')'; }
#endif
template <typename T> ostream &operator<<(ostream &os, const deque<T> &vec) { os << "deq["; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <typename T> ostream &operator<<(ostream &os, const set<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T, typename TH> ostream &operator<<(ostream &os, const unordered_set<T, TH> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T> ostream &operator<<(ostream &os, const multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T> ostream &operator<<(ostream &os, const unordered_multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T1, typename T2> ostream &operator<<(ostream &os, const pair<T1, T2> &pa) { os << '(' << pa.first << ',' << pa.second << ')'; return os; }
template <typename TK, typename TV> ostream &operator<<(ostream &os, const map<TK, TV> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
template <typename TK, typename TV, typename TH> ostream &operator<<(ostream &os, const unordered_map<TK, TV, TH> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
#ifdef HITONANODE_LOCAL
const string COLOR_RESET = "\033[0m", BRIGHT_GREEN = "\033[1;32m", BRIGHT_RED = "\033[1;31m", BRIGHT_CYAN = "\033[1;36m", NORMAL_CROSSED = "\033[0;9;37m", RED_BACKGROUND = "\033[1;41m", NORMAL_FAINT = "\033[0;2m";
#define dbg(x) cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << endl
#define dbgif(cond, x) ((cond) ? cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << endl : cerr)
#else
#define dbg(x) 0
#define dbgif(cond, x) 0
#endif

int main() {
    int N, M;
    cin >> N >> M;
    vector<vector<pint>> to(N);
    while (M--) {
        int a, b, c;
        cin >> a >> b >> c;
        --a, --b;
        to[a].emplace_back(b, c);
    }

    vector<int> vs;
    REP(S, 1 << 20) {
        lint x = 1;
        REP(d, 20) if ((S >> d) & 1) x = lint(x) * (d + 1) / __gcd<lint>(x, d + 1);
        vs.push_back(x);
    }
    vs = sort_unique(vs);
    dbg(vs.size());
    dbg(vs.back());

    constexpr int BS = 960;
    vector<bitset<BS>> reach(N);

    vector Next(BS, vector<int>(21));
    REP(i, BS) {
        lint now = vs[i];
        FOR(e, 1, 21) {
            int nxt = arglb(vs, int(now * e / __gcd<lint>(now, e)));
            Next[i][e] = nxt;
        }
    }

    vector<pint> st;
    reach[0].set(0);
    st.emplace_back(0, 0);
    while (st.size()) {
        auto [now, d] = st.back();
        st.pop_back();
        for (auto [nxt, e] : to[now]) {
            if (!reach[nxt][Next[d][e]]) {
                reach[nxt][Next[d][e]] = 1;
                st.emplace_back(nxt, Next[d][e]);
            }
        }
    }
    if (reach.back().none()) {
        puts("IMPOSSIBLE");
    } else {
        int i = reach.back()._Find_first();
        int j = BS - 1;
        while (!reach.back()[j]) --j;
        cout << vs[i] << ' ' << vs[j] << '\n';
    }
}
accuracy: 1
time_ms: 310
memory_kb: 11536
final_rank: 11

code_snippet: //lcmの種類数は960しかない
#include <iostream>
#include <queue>
#include <set>
#define rep(i, n) for(i = 0; i < n; i++)
using namespace std;

int gcd(int a, int b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}

int lcm(int a, int b) {
	return a / gcd(a, b) * b;
}

int n, m;
vector<int> et[1000];
vector<int> ec[1000];
set<int> dp[1000];

int main() {
	int i;
	
	cin >> n >> m;
	rep(i, m) {
		int a, b, c;
		cin >> a >> b >> c;
		a--; b--;
		et[a].push_back(b);
		ec[a].push_back(c);
	}
	
	typedef pair<int, int> P;
	queue<P> que;
	que.push(P(0, 1));
	dp[0].insert(1);
	
	while (!que.empty()) {
		P now = que.front();
		que.pop();
		int pos = now.first;
		int val = now.second;
		rep(i, et[pos].size()) {
			int nv = et[pos][i];
			int nc = lcm(val, ec[pos][i]);
			if (dp[nv].find(nc) == dp[nv].end()) {
				dp[nv].insert(nc);
				que.push(P(nv, nc));
			}
		}
	}
	
	if (dp[n - 1].size() == 0) { cout << "IMPOSSIBLE" << endl; }
	else { cout << *dp[n - 1].begin() << " " << *dp[n - 1].rbegin() << endl; }
	return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 11656
final_rank: 8

code_snippet: #include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize("Ofast")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
using ll=long long;
#define overload4(_1, _2, _3, _4, name, ...) name
#define rep1(n) for(ll i = 0; i < (n); ++i)
#define rep2(i, n) for(ll i = 0; i < (n); ++i)
#define rep3(i, a, b) for(ll i = (a); i < (b); ++i)
#define rep4(i, a, b, c) for(ll i = (a); i < (b); i += (c))
#define rep(...) overload4(__VA_ARGS__, rep4, rep3, rep2, rep1)(__VA_ARGS__)
#define rrep1(n) for(ll i=n;i--;)
#define rrep2(i,n) for(ll i=n;i--;)
#define rrep3(i,a,b) for(ll i=b;i-->(a);)
#define rrep4(i,a,b,c) for(ll i=(a)+((b)-(a)-1)/(c)*(c);i>=(a);i-=c)
#define rrep(...) overload4(__VA_ARGS__,rrep4,rrep3,rrep2,rrep1)(__VA_ARGS__)
//# define each(i,...) for(auto&& i:__VA_ARGS__)
//#define each1(i,a) for(auto&&i:a)
//#define each2(x,y,a) for(auto&&[x,y]:a)
//#define each3(x,y,z,a) for(auto&&[x,y,z]:a)
//#define each(...) overload4(__VA_ARGS__,each3,each2,each1)(__VA_ARGS__)
#define all(i) begin(i), end(i)
#define rall(i) (i).rbegin(), (i).rend()
#define Sort(a) sort(all(a))
#define Rev(a) reverse(all(a))
#define elif else if
template<class T> bool chmin(T &a, const T &b){ if(a > b){ a = b; return 1; } else return 0; }
template<class T> bool chmax(T &a, const T &b){ if(a < b){ a = b; return 1; } else return 0; }
template<class T> ll sum(const T& a){ return accumulate(all(a), 0LL); }
template<class T> auto min(const T& a){ return *min_element(all(a)); }
template<class T> auto max(const T& a){ return *max_element(all(a)); }
vector<ll> iota(ll n, ll begin = 0){ vector<ll> a(n); iota(a.begin(), a.end(), begin); return a; }
const int INF =0x3fffffff;
const ll INFll =0x1fffffffffffffff;// 4times ok
const int MOD= int(1e9)+7;
const int MODD=998244353;
using P= pair<int,int>;
using Pl= pair<ll,ll>;
using ld=long double;
using VVl=vector<vector<ll>>;
using VVd=vector<vector<ld>>;
using Vd=vector<ld>;
using tuplis =array<ll, 3>;
using quatro=array<ll,4>;
#define vec(type,name,...) vector<type> name(__VA_ARGS__)
#define vv(type,name,h,...) vector<vector<type>>name(h,vector<type>(__VA_ARGS__))
template<class T> using pq = priority_queue<T, vector<T>, greater<T>>;
const ll four[] = {0, 1, 0, -1, 0};
const ll eight[] = {0, 1, 1, 0, -1, -1, 1, -1, 0};
const ld EPS = 1e-9;
const ld PI = 3.1415926535897932;
ll gcd(ll a, ll b){ while(b){ ll c = b; b = a % b; a = c; } return a; }
ll lcm(ll a, ll b){ if(!a || !b) return 0; return a * b / gcd(a, b); }
ll intpow(ll a, ll b){ ll ans = 1; while(b){ if(b & 1) ans *= a; a *= a; b /= 2; } return ans; }
ll modpow(ll a, ll b, ll p){ a%=p; ll ans = 1; while(b){ if(b & 1) (ans *= a) %= p; (a *= a) %= p; b /= 2; } return ans; }
ll isqr(ll x){ll r=sqrt(x)-1;while((r+1)*(r+1)<=x)r++;return r;}
vector<ll> divisor(ll x){vector<ll> ans; for(ll i = 1; i * i <= x; i++) if(x % i == 0) ans.push_back(i); rrep(ans.size() - (ans.back() * ans.back() == x)) ans.push_back(x / ans[i]); return ans; }
int scan(){ return getchar(); }
void scan(int& a){ scanf("%d", &a); }
void scan(unsigned& a){ scanf("%u", &a); }
void scan(long& a){ scanf("%ld", &a); }
void scan(long long& a){ scanf("%lld", &a); }
void scan(unsigned long long& a){ scanf("%llu", &a); }
void scan(char& a){ do{ a = getchar(); }while(a == ' ' || a == '\n'); }
void scan(float& a){ scanf("%f", &a); }
void scan(double& a){ scanf("%lf", &a); }
void scan(long double& a){ scanf("%Lf", &a); }
void scan(vector<bool>& a){ for(unsigned i = 0; i < a.size(); i++){ int b; scan(b); a[i] = b; } }
void scan(char a[]){ scanf("%s", a); }
void scan(string& a){ cin >> a; }
template<class T> void scan(vector<T>&);
template<class T, size_t size> void scan(array<T, size>&);
template<class T, class L> void scan(pair<T, L>&);
template<class T, size_t size> void scan(T(&)[size]);
template<class T> void scan(vector<T>& a){ for(auto&& i : a) scan(i); }
template<class T> void scan(deque<T>& a){ for(auto&& i : a) scan(i); }
template<class T, size_t size> void scan(array<T, size>& a){ for(auto&& i : a) scan(i); }
template<class T, class L> void scan(pair<T, L>& p){ scan(p.first); scan(p.second); }
template<class T, size_t size> void scan(T (&a)[size]){ for(auto&& i : a) scan(i); }
template<class T> void scan(T& a){ cin >> a; }
void in(){}
template <class Head, class... Tail> void in(Head& head, Tail&... tail){ scan(head); in(tail...); }
void print(){ putchar(' '); }
void print(bool a){ printf("%d", a); }
void print(int a){ printf("%d", a); }
void print(unsigned a){ printf("%u", a); }
void print(long a){ printf("%ld", a); }
void print(long long a){ printf("%lld", a); }
void print(unsigned long long a){ printf("%llu", a); }
void print(char a){ printf("%c", a); }
void print(char a[]){ printf("%s", a); }
void print(const char a[]){ printf("%s", a); }
void print(float a){ printf("%.15f", a); }
void print(double a){ printf("%.15f", a); }
void print(long double a){ printf("%.15Lf", a); }
void print(const string& a){ for(auto&& i : a) print(i); }
template<class T> void print(const complex<T>& a){ if(a.real() >= 0) print('+'); print(a.real()); if(a.imag() >= 0) print('+'); print(a.imag()); print('i'); }
template<class T> void print(const vector<T>&);
template<class T, size_t size> void print(const array<T, size>&);
template<class T, class L> void print(const pair<T, L>& p);
template<class T, size_t size> void print(const T (&)[size]);
template<class T> void print(const vector<T>& a){ if(a.empty()) return; print(a[0]); for(auto i = a.begin(); ++i != a.end(); ){ putchar(' '); print(*i); } }
template<class T> void print(const deque<T>& a){ if(a.empty()) return; print(a[0]); for(auto i = a.begin(); ++i != a.end(); ){ putchar(' '); print(*i); } }
template<class T, size_t size> void print(const array<T, size>& a){ print(a[0]); for(auto i = a.begin(); ++i != a.end(); ){ putchar(' '); print(*i); } }
template<class T, class L> void print(const pair<T, L>& p){ print(p.first); putchar(' '); print(p.second); }
template<class T, size_t size> void print(const T (&a)[size]){ print(a[0]); for(auto i = a; ++i != end(a); ){ putchar(' '); print(*i); } }
template<class T> void print(const T& a){ cout << a; }
int out(){ putchar('\n'); return 0; }
template<class T> int out(const T& t){ print(t); putchar('\n'); return 0; }
template<class Head, class... Tail> int out(const Head& head, const Tail&... tail){ print(head); putchar(' '); out(tail...); return 0; }
#define INT(...) int __VA_ARGS__; in(__VA_ARGS__)
#define LL(...) ll __VA_ARGS__; in(__VA_ARGS__)
#define ULL(...) ull __VA_ARGS__; in(__VA_ARGS__)
#define STR(...) string __VA_ARGS__; in(__VA_ARGS__)
#define CHR(...) char __VA_ARGS__; in(__VA_ARGS__)
#define DBL(...) double __VA_ARGS__; in(__VA_ARGS__)
#define LD(...) ld __VA_ARGS__; in(__VA_ARGS__)
#define VEC(type,name,size) vector<type> name(size); in(name)
#define VV(type, name, h, w) vector<vector<type>> name(h, vector<type>(w)); in(name)

void solve(){
   LL(n,m);
   vv(Pl,g,n);
   rep(m){
       LL(a,b,c);
       a--;b--;
       g[a].emplace_back(b,c);
   }
   ll mn=INFll,mx=-1;
   queue<Pl>q;
   q.push({0,1});
   map<Pl,ll>used;
   while(q.size()){
       auto [at,x]=q.front();q.pop();
       if(at==n-1){
           chmin(mn,x);
           chmax(mx,x);
       }
       for(auto [to,cost]:g[at]){
           ll x2=lcm(x,cost);
           if(used[{to,x2}])continue;
           used[{to,x2}]=1;
           q.push({to,x2});
       }
   }
   if(mn==INFll){
       out("IMPOSSIBLE");
   }
   else {
       out(mn, mx);
   }
}

int main(){
    ll t=1;
//    in(t);
    rep(t){
        solve();
    }
    return 0;
}
accuracy: 1
time_ms: 160
memory_kb: 14468
final_rank: 10

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;

#define rep(i,l,r) for(ll i=(l); i<(r); i++)
#define rrep(i,l,r) for(ll i=(r)-1; i>=(l); i--)
#define ALL(c) (c).begin(), (c).end()
#define RALL(c) (c).rbegin(), (c).rend()
#define SORT(c) sort(ALL(c))
#define RSORT(c) sort(RALL(c))
#define MINV(c) *min_element(ALL(c))
#define MAXV(c) *max_element(ALL(c))

using VI = vector<int>;
using VVI = vector<VI>;
using VL = vector<ll>;
using VVL = vector<VL>;
using VS = vector<string>;
using VVS = vector<VS>;
using VB = vector<bool>;
using VVB = vector<VB>;
using VC = vector<char>;
using VVC = vector<VC>;
using VD = vector<ld>;
using VVD = vector<VD>;
using P = pair<ll,ll>;
using VP = vector<P>;
using VVP = vector<VP>;
const ll LINF = 2e18;
const int INF = 2e9;
const int MAX = (1<<14);
const int NMAX = 1000;
using T = tuple<int,int,int,ll>;
int dp[MAX][NMAX][2];


int main(){
  int N, M;
  cin >> N >> M;
  VVP G(N,VP(0));
  rep(i,0,M){
    int a, b, c;
    cin >> a >> b >> c;
    a--; b--;
    G[a].push_back(P(b,c));
  }
  vector<int> L = {2,4,8,16,3,9,5,7,11,13,17,19};
  unordered_map<int,int> mp;
  rep(i,1,21){
    int K = 0;
    int X = i;
    rrep(j,0,L.size()){
      if(X % L[j] == 0){
        K += (1<<j);
        X /= L[j];
      }
    }
    mp[i] = K;
  }
  
  queue<T> Q;
  Q.push({0,0,0,1});
  Q.push({0,0,1,1});
  
  rep(i,0,MAX){
    rep(j,0,NMAX){
      dp[i][j][1] = INF;
    }
  }
  
  dp[0][0][0] = 1;
  dp[0][0][1] = 1;
  
  while(Q.size()){
    auto[a,b,c,d] = Q.front();
    //cout << a << " " << b << " " << c << " " << d << endl;
    Q.pop();
    if(c == 0){
      if(dp[a][b][c] > d){
        continue;
      }
      rep(i,0,G[b].size()){
        auto[x1,x2] = G[b][i];
        if(dp[a|mp[x2]][x1][c] < lcm(dp[a][b][c],x2)){
          dp[a|mp[x2]][x1][c] = lcm(dp[a][b][c],x2);
          Q.push({a|mp[x2], x1, c, dp[a|mp[x2]][x1][c]});
        }
      }
    }
    if(c == 1){
      if(dp[a][b][c] < d){
        continue;
      }
      rep(i,0,G[b].size()){
        auto[x1,x2] = G[b][i];
        if(dp[a|mp[x2]][x1][c] > lcm(dp[a][b][c],x2)){
          dp[a|mp[x2]][x1][c] = lcm(dp[a][b][c],x2);
          Q.push({a|mp[x2], x1, c, dp[a|mp[x2]][x1][c]});
        }
      }
    }
  }
  int A = 0;
  int B = INF;
  rep(i,0,MAX){
    A = max(A,dp[i][N-1][0]);
    B = min(B,dp[i][N-1][1]);
  }
  if(A == 0){
    cout << "IMPOSSIBLE" << endl;
  }
  else{
    cout << B << " " << A << endl;
  }
}
accuracy: 1
time_ms: 530
memory_kb: 137272
final_rank: 15

code_snippet: #include<bits/stdc++.h>
/*
#include<atcoder/all>
using namespace atcoder;
//using mint = modint1000000007;
using mint = modint998244353;
*/
//g++ -I/opt/ac-library ./**.cpp
using namespace std;
using ll = long long;
using ull = unsigned long long;
//const long long MOD = 1000000007;
const long long MOD = 998244353;
const long double PI = 3.14159265358979;
const long long INF = 1LL<<60;
template <typename T> bool chmax(T &a, const T& b){if(a < b){a = b;return true;}return false;}
template <typename T> bool chmin(T &a, const T& b){if(a > b){a = b;return true;}return false;}
#define deb(var) do{cout << #var << " : "; view(var);}while(0)
template<typename T> void view(T e){cout << e << endl;}
void view(vector<string>& v){cout << endl;for(auto& s :v){view(s);}cout << endl;}
template<typename T> void view(vector<T>& v){for(auto& e :v){cout << e << " ";}cout << endl;}
template<typename T> void view(vector<vector<T>>& vv){cout << endl;for(auto& v:vv){view(v);}}
ll gcd(ll a, ll b){if (b == 0) return a;else return gcd(b, a % b);}
ll lcm(ll x,ll y){return ll(x/gcd(x,y))*y;}
template<typename T> using min_priority_queue = priority_queue<T, vector<T>, greater<T>>;
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define int long long

struct Edge {
    int to;
    int w;
    Edge(int to, int w): to(to), w(w) {}
};

int32_t main() {
    int n, m; cin >> n >> m;
    vector<vector<Edge>> g(n);
    for (int i=0; i<m; i++) {
        int a, b, c; cin >> a >> b >> c;
        a--; b--;
        g[a].push_back(Edge(b, c));
    }

    vector<unordered_set<int>> st(n);
    st[0].insert(1);
    queue<pair<int, int>> que;
    que.push({1, 0});
    while (que.size()) {
        auto [cur_l, v] = que.front();
        que.pop();
        for (auto [nxt, c] : g[v]) {
            int l = lcm(cur_l, c);
            if (!st[nxt].count(l)) {
                st[nxt].insert(l);
                que.push({l, nxt});
            }
        }
    }
    int mn = INF;
    int mx = 0;
    for (auto x : st[n - 1]){
        chmin(mn, x);
        chmax(mx, x);
    }
    if (mn == INF) cout << "IMPOSSIBLE" << "\n";
    else cout << mn << " " << mx << "\n";
}
accuracy: 1
time_ms: 70
memory_kb: 10820
final_rank: 4

code_snippet: #define MOD_TYPE 2

#include <bits/stdc++.h>
using namespace std;

#if 0
#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#endif

#if 0
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/rope>
using namespace __gnu_pbds;
using namespace __gnu_cxx;
template <typename T>
using extset =
    tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
#endif

#if 0
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>
using Int = boost::multiprecision::cpp_int;
using lld = boost::multiprecision::cpp_dec_float_100;
#endif

#pragma region Macros

using ll = long long int;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using pld = pair<ld, ld>;
template <typename T>
using smaller_queue = priority_queue<T, vector<T>, greater<T>>;

constexpr ll MOD = (MOD_TYPE == 1 ? (ll)(1e9 + 7) : 998244353);
constexpr int INF = (int)1e9 + 10;
constexpr ll LINF = (ll)4e18;
constexpr ld PI = acos(-1.0);
constexpr ld EPS = 1e-7;
constexpr int Dx[] = {0, 0, -1, 1, -1, 1, -1, 1, 0};
constexpr int Dy[] = {1, -1, 0, 0, -1, -1, 1, 1, 0};

#define REP(i, m, n) for (ll i = m; i < (ll)(n); ++i)
#define rep(i, n) REP(i, 0, n)
#define REPI(i, m, n) for (int i = m; i < (int)(n); ++i)
#define repi(i, n) REPI(i, 0, n)
#define MP make_pair
#define MT make_tuple
#define YES(n) cout << ((n) ? "YES" : "NO") << "\n"
#define Yes(n) cout << ((n) ? "Yes" : "No") << "\n"
#define possible(n) cout << ((n) ? "possible" : "impossible") << "\n"
#define Possible(n) cout << ((n) ? "Possible" : "Impossible") << "\n"
#define all(v) v.begin(), v.end()
#define NP(v) next_permutation(all(v))
#define dbg(x) cerr << #x << ":" << x << "\n";
#define UNIQUE(v) v.erase(unique(all(v)), v.end())

struct io_init {
  io_init() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << setprecision(30) << setiosflags(ios::fixed);
  };
} io_init;
template <typename T>
inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
template <typename T>
inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
inline ll CEIL(ll a, ll b) { return (a + b - 1) / b; }
template <typename A, size_t N, typename T>
inline void Fill(A (&array)[N], const T &val) {
  fill((T *)array, (T *)(array + N), val);
}
template <typename T>
vector<T> compress(vector<T> &v) {
  vector<T> val = v;
  sort(all(val)), val.erase(unique(all(val)), val.end());
  for (auto &&vi : v) vi = lower_bound(all(val), vi) - val.begin();
  return val;
}
template <typename T, typename U>
constexpr istream &operator>>(istream &is, pair<T, U> &p) noexcept {
  is >> p.first >> p.second;
  return is;
}
template <typename T, typename U>
constexpr ostream &operator<<(ostream &os, pair<T, U> &p) noexcept {
  os << p.first << " " << p.second;
  return os;
}
template <typename T>
constexpr istream &operator>>(istream &is, vector<T> &v) noexcept {
  for (int i = 0; i < v.size(); i++) is >> v[i];
  return is;
}
template <typename T>
constexpr ostream &operator<<(ostream &os, vector<T> &v) noexcept {
  for (int i = 0; i < v.size(); i++)
    os << v[i] << (i + 1 == v.size() ? "" : " ");
  return os;
}
template <typename T>
constexpr void operator--(vector<T> &v, int) noexcept {
  for (int i = 0; i < v.size(); i++) v[i]--;
}
random_device seed_gen;
mt19937_64 engine(seed_gen());

#pragma endregion

// --------------------------------------

struct BFS {
  vector<int> d;
  vector<vector<pair<int, int>>> E;
  BFS(int V) : d(V, INF), E(V) {}

  void add_edge(int u, int v, bool c, bool directed = true) {
    E[u].push_back({v, c});
    if (!directed) E[v].push_back({u, c});
  }

  void calc(int s) {
    d[s] = 0;
    deque<int> que;
    que.push_front(s);
    while (!que.empty()) {
      int u = que.front();
      que.pop_front();
      for (auto [v, c] : E[u]) {
        if (d[v] <= d[u] + c) continue;
        d[v] = d[u] + c;
        if (!c)
          que.push_front(v);
        else
          que.push_back(v);
      }
    }
  }
};

vector<int> primes = {2, 3, 5, 7, 11, 13, 17, 19};
vector<int> lcms;

void dfs(int i, ll p) {
  if (i == primes.size()) {
    lcms.push_back(p);
    return;
  }
  if (i == 0) {
    dfs(i + 1, p);
    dfs(i + 1, p * 2);
    dfs(i + 1, p * 4);
    dfs(i + 1, p * 8);
    dfs(i + 1, p * 16);
  } else if (i == 1) {
    dfs(i + 1, p);
    dfs(i + 1, p * 3);
    dfs(i + 1, p * 9);
  } else {
    dfs(i + 1, p);
    dfs(i + 1, p * primes[i]);
  }
}

void solve() {
  dfs(0, 1);

  int n, m;
  cin >> n >> m;
  vector<vector<pii>> E(n);
  rep(i, m) {
    int a, b, c;
    cin >> a >> b >> c;
    a--, b--;
    E[a].push_back({b, c});
  }

  map<int, int> mp;
  for (auto l : lcms) mp[l] = 0;
  int z = 0;
  for (auto &&[k, v] : mp) v = z++;

  BFS bfs(n * lcms.size());
  auto f = [&](int i, int l) { return i * lcms.size() + mp[l]; };
  rep(i, n) for (auto l : lcms) {
    for (auto [j, c] : E[i]) {
      int l2 = lcm(l, c);
      bfs.add_edge(f(i, l), f(j, l2), 0);
    }
  }
  bfs.calc(0);
  int Min = INF, Max = -1;
  for (auto l : lcms) {
    if (bfs.d[f(n - 1, l)] != INF) {
      chmin(Min, l);
      chmax(Max, l);
    }
  }
  if (Min == INF) {
    cout << "IMPOSSIBLE\n";
  } else {
    cout << Min << " " << Max << "\n";
  }
}

int main() { solve(); }
accuracy: 1
time_ms: 260
memory_kb: 62052
final_rank: 14

code_snippet: #define MOD_TYPE 2

#include <bits/stdc++.h>
using namespace std;

#if 0
#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#endif

#if 0
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/rope>
using namespace __gnu_pbds;
using namespace __gnu_cxx;
template <typename T>
using extset =
    tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
#endif

#if 0
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>
using Int = boost::multiprecision::cpp_int;
using lld = boost::multiprecision::cpp_dec_float_100;
#endif

#pragma region Macros

using ll = long long int;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using pld = pair<ld, ld>;
template <typename T>
using smaller_queue = priority_queue<T, vector<T>, greater<T>>;

constexpr ll MOD = (MOD_TYPE == 1 ? (ll)(1e9 + 7) : 998244353);
constexpr int INF = (int)1e9 + 10;
constexpr ll LINF = (ll)4e18;
constexpr ld PI = acos(-1.0);
constexpr ld EPS = 1e-7;
constexpr int Dx[] = {0, 0, -1, 1, -1, 1, -1, 1, 0};
constexpr int Dy[] = {1, -1, 0, 0, -1, -1, 1, 1, 0};

#define REP(i, m, n) for (ll i = m; i < (ll)(n); ++i)
#define rep(i, n) REP(i, 0, n)
#define REPI(i, m, n) for (int i = m; i < (int)(n); ++i)
#define repi(i, n) REPI(i, 0, n)
#define MP make_pair
#define MT make_tuple
#define YES(n) cout << ((n) ? "YES" : "NO") << "\n"
#define Yes(n) cout << ((n) ? "Yes" : "No") << "\n"
#define possible(n) cout << ((n) ? "possible" : "impossible") << "\n"
#define Possible(n) cout << ((n) ? "Possible" : "Impossible") << "\n"
#define all(v) v.begin(), v.end()
#define NP(v) next_permutation(all(v))
#define dbg(x) cerr << #x << ":" << x << "\n";
#define UNIQUE(v) v.erase(unique(all(v)), v.end())

struct io_init {
  io_init() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << setprecision(30) << setiosflags(ios::fixed);
  };
} io_init;
template <typename T>
inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
template <typename T>
inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
inline ll CEIL(ll a, ll b) { return (a + b - 1) / b; }
template <typename A, size_t N, typename T>
inline void Fill(A (&array)[N], const T &val) {
  fill((T *)array, (T *)(array + N), val);
}
template <typename T>
vector<T> compress(vector<T> &v) {
  vector<T> val = v;
  sort(all(val)), val.erase(unique(all(val)), val.end());
  for (auto &&vi : v) vi = lower_bound(all(val), vi) - val.begin();
  return val;
}
template <typename T, typename U>
constexpr istream &operator>>(istream &is, pair<T, U> &p) noexcept {
  is >> p.first >> p.second;
  return is;
}
template <typename T, typename U>
constexpr ostream &operator<<(ostream &os, pair<T, U> &p) noexcept {
  os << p.first << " " << p.second;
  return os;
}
template <typename T>
constexpr istream &operator>>(istream &is, vector<T> &v) noexcept {
  for (int i = 0; i < v.size(); i++) is >> v[i];
  return is;
}
template <typename T>
constexpr ostream &operator<<(ostream &os, vector<T> &v) noexcept {
  for (int i = 0; i < v.size(); i++)
    os << v[i] << (i + 1 == v.size() ? "" : " ");
  return os;
}
template <typename T>
constexpr void operator--(vector<T> &v, int) noexcept {
  for (int i = 0; i < v.size(); i++) v[i]--;
}
random_device seed_gen;
mt19937_64 engine(seed_gen());

#pragma endregion

// --------------------------------------

struct BFS {
  vector<int> d;
  vector<vector<pair<int, int>>> E;
  BFS(int V) : d(V, INF), E(V) {}

  void add_edge(int u, int v, bool c, bool directed = true) {
    E[u].push_back({v, c});
    if (!directed) E[v].push_back({u, c});
  }

  void calc(int s) {
    d[s] = 0;
    deque<int> que;
    que.push_front(s);
    while (!que.empty()) {
      int u = que.front();
      que.pop_front();
      for (auto [v, c] : E[u]) {
        if (d[v] <= d[u] + c) continue;
        d[v] = d[u] + c;
        if (!c)
          que.push_front(v);
        else
          que.push_back(v);
      }
    }
  }
};

vector<int> primes = {2, 3, 5, 7, 11, 13, 17, 19};
vector<int> lcms;

void dfs(int i, ll p) {
  if (i == primes.size()) {
    lcms.push_back(p);
    return;
  }
  if (i == 0) {
    dfs(i + 1, p);
    dfs(i + 1, p * 2);
    dfs(i + 1, p * 4);
    dfs(i + 1, p * 8);
  } else if (i == 1) {
    dfs(i + 1, p);
    dfs(i + 1, p * 3);
    dfs(i + 1, p * 9);
  } else {
    dfs(i + 1, p);
    dfs(i + 1, p * primes[i]);
  }
}

void solve() {
  dfs(0, 1);

  int n, m;
  cin >> n >> m;
  vector<vector<pii>> E(n);
  rep(i, m) {
    int a, b, c;
    cin >> a >> b >> c;
    a--, b--;
    E[a].push_back({b, c});
  }

  map<int, int> mp;
  for (auto l : lcms) mp[l] = 0;
  int z = 0;
  for (auto &&[k, v] : mp) v = z++;

  BFS bfs(n * lcms.size());
  auto f = [&](int i, int l) { return i * lcms.size() + mp[l]; };
  rep(i, n) for (auto l : lcms) {
    for (auto [j, c] : E[i]) {
      int l2 = lcm(l, c);
      bfs.add_edge(f(i, l), f(j, l2), 0);
    }
  }
  bfs.calc(0);
  int Min = INF, Max = -1;
  for (auto l : lcms) {
    if (bfs.d[f(n - 1, l)] != INF) {
      chmin(Min, l);
      chmax(Max, l);
    }
  }
  if (Min == INF) {
    cout << "IMPOSSIBLE\n";
  } else {
    cout << Min << " " << Max << "\n";
  }
}

int main() { solve(); }
accuracy: 0.3170731707317073
time_ms: 70
memory_kb: 13484
final_rank: 20

code_snippet: #include<bits/stdc++.h> 
using namespace std;
using ll=long long int;
using Int=__int128;
#define ALL(A) A.begin(),A.end()
template<typename T1,typename T2> bool chmin(T1 &a,T2 b){if(a<=b)return 0; a=b; return 1;}
template<typename T1,typename T2> bool chmax(T1 &a,T2 b){if(a>=b)return 0; a=b; return 1;}
template<typename T> constexpr int bitUP(T x,int a){return (x>>a)&1;}
//→　↓　←　↑ 
int dh[4]={1,1,-1,-1}, dw[4]={1,-1,1,-1};
//右上から時計回り
//int dh[8]={-1,0,1,1,1,0,-1,-1}, dw[8]={1,1,1,0,-1,-1,-1,0};
long double EPS = 1e-6;
long double PI = acos(-1);
const ll INF=(1LL<<62);
const int MAX=(1<<30);
//constexpr ll MOD=1000000000+7;
constexpr ll MOD=998244353;


inline void bin101(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(20);
}

using pii=pair<int,int>;
using pil=pair<int,ll>;
using pli=pair<ll,int>;
using pll=pair<ll,ll>;
using psi=pair<string,int>;
using pis=pair<int,string>;
using psl=pair<string,ll>;
using pls=pair<ll,string>;
using pss=pair<string,string>;

using Graph=vector<vector<int>>;

template<typename T >
struct edge {
	int to;
	T cost;
	edge()=default;
	edge(int to, T cost) : to(to), cost(cost) {}

};
template<typename T>
using WeightGraph=vector<vector<edge<T>>>;

template<typename T>
void CinGraph(int M,WeightGraph<T> &g,bool directed=false,bool index1=true){
    while(M--){
        int s,t;
        T cost;
        cin>>s>>t>>cost;
        if(index1) s--,t--;
        g[s].emplace_back(t,cost);
        if(not directed) g[t].emplace_back(s,cost);
    }
}

void CinGraph(int M,Graph &g,bool directed=false,bool index1=true){
    while(M--){
        int s,t;
        cin>>s>>t;
        if(index1) s--,t--;
        g[s].push_back(t);
        if(not directed) g[t].push_back(s);
    }
}


//0-indexed vector cin
template<typename T>
inline istream &operator>>(istream &is,vector<T> &v) {
    for(size_t i=0;i<v.size();i++) is>>v[i];
	return is;
}
 
//0-indexed vector cin
template<typename T>
inline istream &operator>>(istream &is,vector<vector<T>> &v) {
    for(size_t i=0;i<v.size();i++){
        is>>v[i];
    }
    return is;
}
//vector cout
template<typename T>
inline ostream &operator<<(ostream &os,const vector<T> &v) {
    bool sp=true;
    if(string(typeid(T).name())=="c") sp=false;
    for(size_t i=0;i<v.size();i++){
        if(i and sp) os<<" ";
        os<<v[i];
    }
    return os;
}
//vector<vector> cout
template<typename T>
inline ostream &operator<<(ostream &os,const vector<vector<T>> &v) {
    for(size_t i=0;i<v.size();i++){
        os<<v[i];
        if(i+1!=v.size()) os<<"\n";
    }
    return os;
}

//Graph out
template<typename T>
inline ostream &operator<<(ostream &os,const Graph &g) {
    for(size_t i=0;i<g.size();i++){
        for(int to:g[i]){
            os<<i<<"->"<<to<<" ";
        }
        os<<endl;
    }
    return os;
}

//WeightGraph out
template<typename T>
inline ostream &operator<<(ostream &os,const WeightGraph<T> &g) {
    for(size_t i=0;i<g.size();i++){
        for(auto e:g[i]){
            os<<i<<"->"<<e.to<<"("<<e.cost<<") ";
        }
        os<<endl;
    }
    return os;
}


//要素数n 初期値x
template<typename T>
inline vector<T> vmake(size_t n,T x){
	return vector<T>(n,x);
}

//a,b,c,x data[a][b][c] 初期値x
template<typename... Args>
auto vmake(size_t n,Args... args){
	auto v=vmake(args...);
	return vector<decltype(v)>(n,move(v));
}
template<typename V,typename T>
void fill(V &v,const T value){
    v=value;
}

template<typename V,typename T>
void fill(vector<V> &vec,const T value){
    for(auto &v:vec) fill(v,value);
}

//pair cout
template<typename T, typename U>
inline ostream &operator<<(ostream &os,const pair<T,U> &p) {
	os<<p.first<<" "<<p.second;
	return os;
}
 
//pair cin
template<typename T, typename U>
inline istream &operator>>(istream &is,pair<T,U> &p) {
	is>>p.first>>p.second;
	return is;
}
 
//ソート
template<typename T>
inline void vsort(vector<T> &v){
    sort(v.begin(),v.end());
}
 
//逆順ソート
template<typename T>
inline void rvsort(vector<T> &v){
	sort(v.rbegin(),v.rend());
}

//1ビットの数を返す
inline int popcount(int x){
	return __builtin_popcount(x);
}
//1ビットの数を返す
inline int popcount(ll x){
	return __builtin_popcountll(x);
}
template<typename T>
inline void Compress(vector<T> &C){
    sort(C.begin(),C.end());
    C.erase(unique(C.begin(),C.end()),C.end());
}
template<typename T>
inline int lower_idx(const vector<T> &C,T value){
    return lower_bound(C.begin(),C.end(),value)-C.begin();
}
template<typename T>
inline int upper_idx(const vector<T> &C,T value){
    return upper_bound(C.begin(),C.end(),value)-C.begin();
}
//時計回りに90度回転
template<typename T>
inline void rotate90(vector<vector<T>> &C){
    vector<vector<T>> D(C[0].size(),vector<T>(C.size()));
    for(int h=0;h<C.size();h++){
        for(int w=0;w<C[h].size();w++){
            D[w][C.size()-1-h]=C[h][w];
        }
    }
    C=D;
}
//補グラフを返す
//i→iのような辺は加えない
Graph ComplementGraph(const Graph &g){
    size_t sz=g.size();
    bool used[sz][sz];
    fill(used[0],used[sz],false);
    for(size_t i=0;i<sz;i++){
        for(int to:g[i]){
            used[i][to]=true;
        }
    }
    Graph ret(sz);
    for(size_t i=0;i<sz;i++){
        for(size_t j=0;j<sz;j++){
            if(used[i][j]) continue;
            if(i==j) continue;
            ret[i].push_back(j);
        }
    }
    return ret;
}
//グラフの分解 secondはある頂点がどこに対応するか id[i]={2,3}のとき,頂点iはret[2][3]に対応
//無効グラフのみに対応
pair<vector<Graph>,vector<pair<int,int>>> GraphDecomposition(const Graph &g){
    vector<pair<int,int>> id(g.size(),pair<int,int>(-1,-1));
    vector<Graph> ret;
    vector<int> now;
    for(size_t i=0;i<g.size();i++){
        if(id[i].first!=-1) continue;
        id[i].first=ret.size();
        id[i].second=0;
        now.push_back(i);
        for(size_t j=0;j<now.size();j++){
            for(int to:g[now[j]]){
                if(id[to].first==-1){
                    id[to].first=ret.size();
                    id[to].second=now.size();
                    now.push_back(to);
                }
            }
        }
        Graph r(now.size());
        for(size_t j=0;j<now.size();j++){
            r[j]=g[now[j]];
            for(int &to:r[j]){
                to=id[to].second;
            }
        }
        ret.push_back(r);
        now.clear();
    }
    return make_pair(ret,id);
}
//0indexを想定
bool OutGrid(ll h,ll w,ll H,ll W){
    return (h>=H or w>=W or h<0 or w<0);
}

void NO(){
    cout<<"NO"<<"\n";
}
void YES(){
    cout<<"YES"<<"\n";
}
void No(){
    cout<<"No"<<"\n";
}
void Yes(){
    cout<<"Yes"<<"\n";
}
namespace overflow{
    template<typename T>
    T max(){
        return numeric_limits<T>::max();
    }
    template<typename T>
    T ADD(T a,T b){
        T res;
        return __builtin_add_overflow(a,b,&res)?max<T>():res;
    }
    template<typename T>
    T MUL(T a,T b){
        T res;
        return __builtin_mul_overflow(a,b,&res)?max<T>():res;
    }
};
using namespace overflow;
struct mint{
    using u64=uint_fast64_t;
    u64 a;
    constexpr mint() :a(0){}
    constexpr mint(ll x) :a((x>=0)?(x%MOD):(x%MOD+MOD) ) {}

    inline constexpr mint operator+(const mint rhs)const noexcept{
        return mint(*this)+=rhs;
    }
    inline constexpr mint operator-(const mint rhs)const noexcept{
        return mint(*this)-=rhs;
    }
    inline constexpr mint operator*(const mint rhs)const noexcept{
        return mint(*this)*=rhs;
    }
    inline constexpr mint operator/(const mint rhs)const noexcept{
        return mint(*this)/=rhs;
    }
    inline constexpr mint operator+(const ll rhs) const noexcept{
        return mint(*this)+=mint(rhs);
    }
    inline constexpr mint operator-(const ll rhs)const noexcept{
        return mint(*this)-=mint(rhs);
    }
    inline constexpr mint operator*(const ll rhs)const noexcept{
        return mint(*this)*=mint(rhs);
    }
    inline constexpr mint operator/(const ll rhs)const noexcept{
        return mint(*this)/=mint(rhs);
    }

    inline constexpr mint &operator+=(const mint rhs)noexcept{
        a+=rhs.a;
        if(a>=MOD) a-=MOD;
        return *this;
    }
    inline constexpr mint &operator-=(const mint rhs)noexcept{
        if(rhs.a>a) a+=MOD;
        a-=rhs.a;
        return *this;
    }
    inline constexpr mint &operator*=(const mint rhs)noexcept{
        a=(a*rhs.a)%MOD;
        return *this;
    }
    inline constexpr mint &operator/=(mint rhs)noexcept{
        a=(a*rhs.inverse().a)%MOD;
        return *this;
    }
    inline constexpr mint &operator+=(const ll rhs)noexcept{
        return *this+=mint(rhs);
    }
    inline constexpr mint &operator-=(const ll rhs)noexcept{
        return *this-=mint(rhs);
    }
    inline constexpr mint &operator*=(const ll rhs)noexcept{
        return *this*=mint(rhs);
    }
    inline constexpr mint &operator/=(const ll rhs)noexcept{
        return *this/=mint(rhs);
    }

    inline constexpr mint operator=(const ll x)noexcept{
        a=(x>=0)?(x%MOD):(x%MOD+MOD);
        return *this;
    }

    inline constexpr bool operator==(const mint p)const noexcept{
        return a==p.a;
    }

    inline constexpr bool operator!=(const mint p)const noexcept{
        return a!=p.a;
    }

    inline constexpr mint pow(ll N) const noexcept{
        mint ans(1LL),p(a);
        while(N>0){
            if(bitUP(N,0)){
                ans*=p;
            }
            p*=p;
            N>>=1;
        }
        return ans;
    }
    inline constexpr mint inverse() const noexcept{
        return pow(MOD-2);
    }

};
inline constexpr mint operator+(const ll &a,const mint &b)noexcept{
    return mint(a)+=b;
}
inline constexpr mint operator-(const ll &a,const mint &b)noexcept{
    return mint(a)-=b;
}
inline constexpr mint operator*(const ll &a,const mint &b)noexcept{
    return mint(a)*=b;
}
inline constexpr mint operator/(const ll &a,const mint &b)noexcept{
    return mint(a)/=b;
}
//cout
inline ostream &operator<<(ostream &os,const mint &p) {
    return os<<p.a;
}

//cin
inline istream &operator>>(istream &is,mint &p) {
    ll t;
    is>>t;
    p=t;
    return is;
}

struct Binominal{
    vector<mint> fac,finv,inv; //fac[n]:n! finv:(n!)の逆元
    int sz;
    Binominal(int n=10) :sz(1){
        if(n<=0) n=10;
        init(n);
    }
    inline void init(int n){
        fac.resize(n+1,1);
        finv.resize(n+1,1);
        inv.resize(n+1,1);
        for(int i=sz+1;i<=n;i++){
            fac[i]=fac[i-1]*i;
            inv[i]=MOD-inv[MOD%i]*(MOD/i);
            finv[i]=finv[i-1]*inv[i];
        }
        sz=n;
    }
    //nCk(n,k<=N) をO(1)で求める
    inline mint com(int n,int k){
        if(n<k) return mint(0);
        if(n<0 || k<0) return mint(0);
        if(n>sz) init(n);
        return fac[n]*finv[k]*finv[n-k];
    }
    //nCk(k<=N) をO(k)　で求める 
    inline mint rcom(ll n,int k){
        if(n<0 || k<0 || n<k) return mint(0);
        if(k>sz) init(k);
        mint ret(1);
        for(int i=0;i<k;i++){
            ret*=n-i;
        }
        ret*=finv[k];
        return ret;
    }

    //重複組み合わせ n種類のものから重複を許してk個を選ぶ
    //〇がn個,|がk個
    inline mint h(int n,int k){
        return com(n+k-1,k);
    }
    //順列の公式
    inline mint P(int n,int k){
        if(n<k) return 0;
        if(n>sz) init(n);
        return fac[n]*finv[n-k];
    }

};
vector<int> Subset(int S,bool zero=false,bool full=false){
    vector<int> ret;
    int now=(S-1)&S;
    if(full and S){
        ret.push_back(S);
    }
    do{
        ret.push_back(now);
        now=(now-1)&S;
    }while(now!=0);
    if(zero){
        ret.push_back(0);
    }
    return ret;
}
template<typename T>
T SUM(const vector<T> &v,int s,int t){
    chmax(s,0);
    chmin(t,int(v.size())-1);
    if(s>t) return 0;
    if(s==0) return v[t];
    else return v[t]-v[s-1];
}
template<typename T>
void buildSUM(vector<T> &v){
    for(size_t i=1;i<v.size();i++){
        v[i]+=v[i-1];
    }
    return;
}

//erase_vertexの頂点を消して
//インデックスを調整したグラフを返す
Graph EraseVertex(Graph graph,vector<int> erase_vertex){
    vector<bool> erase(graph.size(),false);
    vector<int> sum(graph.size(),0);
    int cnt_erase=0;
    for(int idx:erase_vertex){
        if(erase[idx]) continue;
        cnt_erase++;
        erase[idx]=true;
        sum[idx]++;
    }
    for(int i=1;i<(int)sum.size();i++){
        sum[i]+=sum[i-1];
    }
    for(int i=0;i<int(graph.size())-cnt_erase;i++){
        int pre_i=i+sum[i];
        graph[i].clear();
        for(int to:graph[pre_i]){
            if(erase[to]) continue;
            graph[i].push_back(to-sum[to]);
        }
    }
    return graph;
}
//無限大(閉路がある)の場合、-1を返す
//O(V+E)
int LongestPath(const vector<vector<int>> &g){
    int sz=(int)g.size();
    vector<int> dp(sz,0);
    vector<int> cnt(sz,0);
    stack<int> st;
    for(int i=0;i<sz;i++){
        for(auto &e:g[i]){
            cnt[e]++;
        }
    }
    for(int i=0;i<sz;i++){
        if(cnt[i]==0) st.push(i);
    }
    int used=0;
    while(!st.empty()){
        int now=st.top(); st.pop();
        for(auto &e:g[now]){
            if(max(dp[e],dp[now]+1)){
                dp[e]=dp[now]+1;
            }
            cnt[e]--;
            if(cnt[e]==0) st.push(e);
        }
        used++;
    }
    if(used!=sz) return -1;
    int ret=0;
    for(int i=0;i<sz;i++){
        if(ret<dp[i]){
            ret=dp[i];
        }
    }
    return ret; 
}
string f(string c1){
    if(c1.size()==1){
        c1=char('A'-1)+c1;
    }
    return c1;
}
int g(string c1){
    return (c1[0]-char('A'-1))*26+c1[1];
}
#define D(a) D[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]][a[9]]
//           2,3,,5,,7,,11,13,15,,17,19
bool D[1100][5][3][2][2][2][2][2][2][2];

using A=array<int,10>;

void solve(){
    int N,M;
    cin>>N>>M;
    WeightGraph<int> G(N);
    for(int i=0;i<M;i++){
        int a,b,c; 
        cin>>a>>b>>c; 
        a--; b--;
        G[a].emplace_back(b,c);
    }
    vector<int> d={2,3,5,7,11,13,15,17,19};
    vector<vector<int>> g(21);
    for(int i=0;i<21;i++){
        for(int x:d){
            int cnt=0;
            int z=i;
            while(z and z%x==0){
                cnt++;
                z/=x;
            }
            g[i].push_back(cnt);
        }
    }
    queue<A> que;
    A a;
    for(int i=0;i<10;i++) a[i]=0;
    que.emplace(a);
    ll min_ans=max<ll>();
    ll max_ans=-1;
    while(que.size()){
        A now=que.front();
        if(now[0]==N-1){
            ll t=1;
            for(int i=0;i<d.size();i++){
                for(int j=0;j<now[i+1];j++) if(d[i]!=15)t*=d[i]; 
            }
            chmin(min_ans,t);
            chmax(max_ans,t);
        }
        que.pop();
        for(auto e:G[now[0]]){
            auto next=now;
            next[0]=e.to;
            for(int i=0;i<d.size();i++){
                chmax(next[i+1],g[e.cost][i]);
            }
            if(D(next)) continue;
            D(next)=true;
            que.push(next);
        }
    }
    if(max_ans==-1) cout<<"IMPOSSIBLE"<<endl;
    else cout<<min_ans<<" "<<max_ans<<endl;
}

int main(){
    bin101();
    int T=1;
    //cin>>T;
    while(T--) solve();
}
accuracy: 1
time_ms: 30
memory_kb: 6688
final_rank: 1

code_snippet: #include<bits/stdc++.h> 
using namespace std;
using ll=long long int;
using Int=__int128;
#define ALL(A) A.begin(),A.end()
template<typename T1,typename T2> bool chmin(T1 &a,T2 b){if(a<=b)return 0; a=b; return 1;}
template<typename T1,typename T2> bool chmax(T1 &a,T2 b){if(a>=b)return 0; a=b; return 1;}
template<typename T> constexpr int bitUP(T x,int a){return (x>>a)&1;}
//→　↓　←　↑ 
int dh[4]={1,1,-1,-1}, dw[4]={1,-1,1,-1};
//右上から時計回り
//int dh[8]={-1,0,1,1,1,0,-1,-1}, dw[8]={1,1,1,0,-1,-1,-1,0};
long double EPS = 1e-6;
long double PI = acos(-1);
const ll INF=(1LL<<62);
const int MAX=(1<<30);
//constexpr ll MOD=1000000000+7;
constexpr ll MOD=998244353;


inline void bin101(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(20);
}

using pii=pair<int,int>;
using pil=pair<int,ll>;
using pli=pair<ll,int>;
using pll=pair<ll,ll>;
using psi=pair<string,int>;
using pis=pair<int,string>;
using psl=pair<string,ll>;
using pls=pair<ll,string>;
using pss=pair<string,string>;

using Graph=vector<vector<int>>;

template<typename T >
struct edge {
	int to;
	T cost;
	edge()=default;
	edge(int to, T cost) : to(to), cost(cost) {}

};
template<typename T>
using WeightGraph=vector<vector<edge<T>>>;

template<typename T>
void CinGraph(int M,WeightGraph<T> &g,bool directed=false,bool index1=true){
    while(M--){
        int s,t;
        T cost;
        cin>>s>>t>>cost;
        if(index1) s--,t--;
        g[s].emplace_back(t,cost);
        if(not directed) g[t].emplace_back(s,cost);
    }
}

void CinGraph(int M,Graph &g,bool directed=false,bool index1=true){
    while(M--){
        int s,t;
        cin>>s>>t;
        if(index1) s--,t--;
        g[s].push_back(t);
        if(not directed) g[t].push_back(s);
    }
}


//0-indexed vector cin
template<typename T>
inline istream &operator>>(istream &is,vector<T> &v) {
    for(size_t i=0;i<v.size();i++) is>>v[i];
	return is;
}
 
//0-indexed vector cin
template<typename T>
inline istream &operator>>(istream &is,vector<vector<T>> &v) {
    for(size_t i=0;i<v.size();i++){
        is>>v[i];
    }
    return is;
}
//vector cout
template<typename T>
inline ostream &operator<<(ostream &os,const vector<T> &v) {
    bool sp=true;
    if(string(typeid(T).name())=="c") sp=false;
    for(size_t i=0;i<v.size();i++){
        if(i and sp) os<<" ";
        os<<v[i];
    }
    return os;
}
//vector<vector> cout
template<typename T>
inline ostream &operator<<(ostream &os,const vector<vector<T>> &v) {
    for(size_t i=0;i<v.size();i++){
        os<<v[i];
        if(i+1!=v.size()) os<<"\n";
    }
    return os;
}

//Graph out
template<typename T>
inline ostream &operator<<(ostream &os,const Graph &g) {
    for(size_t i=0;i<g.size();i++){
        for(int to:g[i]){
            os<<i<<"->"<<to<<" ";
        }
        os<<endl;
    }
    return os;
}

//WeightGraph out
template<typename T>
inline ostream &operator<<(ostream &os,const WeightGraph<T> &g) {
    for(size_t i=0;i<g.size();i++){
        for(auto e:g[i]){
            os<<i<<"->"<<e.to<<"("<<e.cost<<") ";
        }
        os<<endl;
    }
    return os;
}


//要素数n 初期値x
template<typename T>
inline vector<T> vmake(size_t n,T x){
	return vector<T>(n,x);
}

//a,b,c,x data[a][b][c] 初期値x
template<typename... Args>
auto vmake(size_t n,Args... args){
	auto v=vmake(args...);
	return vector<decltype(v)>(n,move(v));
}
template<typename V,typename T>
void fill(V &v,const T value){
    v=value;
}

template<typename V,typename T>
void fill(vector<V> &vec,const T value){
    for(auto &v:vec) fill(v,value);
}

//pair cout
template<typename T, typename U>
inline ostream &operator<<(ostream &os,const pair<T,U> &p) {
	os<<p.first<<" "<<p.second;
	return os;
}
 
//pair cin
template<typename T, typename U>
inline istream &operator>>(istream &is,pair<T,U> &p) {
	is>>p.first>>p.second;
	return is;
}
 
//ソート
template<typename T>
inline void vsort(vector<T> &v){
    sort(v.begin(),v.end());
}
 
//逆順ソート
template<typename T>
inline void rvsort(vector<T> &v){
	sort(v.rbegin(),v.rend());
}

//1ビットの数を返す
inline int popcount(int x){
	return __builtin_popcount(x);
}
//1ビットの数を返す
inline int popcount(ll x){
	return __builtin_popcountll(x);
}
template<typename T>
inline void Compress(vector<T> &C){
    sort(C.begin(),C.end());
    C.erase(unique(C.begin(),C.end()),C.end());
}
template<typename T>
inline int lower_idx(const vector<T> &C,T value){
    return lower_bound(C.begin(),C.end(),value)-C.begin();
}
template<typename T>
inline int upper_idx(const vector<T> &C,T value){
    return upper_bound(C.begin(),C.end(),value)-C.begin();
}
//時計回りに90度回転
template<typename T>
inline void rotate90(vector<vector<T>> &C){
    vector<vector<T>> D(C[0].size(),vector<T>(C.size()));
    for(int h=0;h<C.size();h++){
        for(int w=0;w<C[h].size();w++){
            D[w][C.size()-1-h]=C[h][w];
        }
    }
    C=D;
}
//補グラフを返す
//i→iのような辺は加えない
Graph ComplementGraph(const Graph &g){
    size_t sz=g.size();
    bool used[sz][sz];
    fill(used[0],used[sz],false);
    for(size_t i=0;i<sz;i++){
        for(int to:g[i]){
            used[i][to]=true;
        }
    }
    Graph ret(sz);
    for(size_t i=0;i<sz;i++){
        for(size_t j=0;j<sz;j++){
            if(used[i][j]) continue;
            if(i==j) continue;
            ret[i].push_back(j);
        }
    }
    return ret;
}
//グラフの分解 secondはある頂点がどこに対応するか id[i]={2,3}のとき,頂点iはret[2][3]に対応
//無効グラフのみに対応
pair<vector<Graph>,vector<pair<int,int>>> GraphDecomposition(const Graph &g){
    vector<pair<int,int>> id(g.size(),pair<int,int>(-1,-1));
    vector<Graph> ret;
    vector<int> now;
    for(size_t i=0;i<g.size();i++){
        if(id[i].first!=-1) continue;
        id[i].first=ret.size();
        id[i].second=0;
        now.push_back(i);
        for(size_t j=0;j<now.size();j++){
            for(int to:g[now[j]]){
                if(id[to].first==-1){
                    id[to].first=ret.size();
                    id[to].second=now.size();
                    now.push_back(to);
                }
            }
        }
        Graph r(now.size());
        for(size_t j=0;j<now.size();j++){
            r[j]=g[now[j]];
            for(int &to:r[j]){
                to=id[to].second;
            }
        }
        ret.push_back(r);
        now.clear();
    }
    return make_pair(ret,id);
}
//0indexを想定
bool OutGrid(ll h,ll w,ll H,ll W){
    return (h>=H or w>=W or h<0 or w<0);
}

void NO(){
    cout<<"NO"<<"\n";
}
void YES(){
    cout<<"YES"<<"\n";
}
void No(){
    cout<<"No"<<"\n";
}
void Yes(){
    cout<<"Yes"<<"\n";
}
namespace overflow{
    template<typename T>
    T max(){
        return numeric_limits<T>::max();
    }
    template<typename T>
    T ADD(T a,T b){
        T res;
        return __builtin_add_overflow(a,b,&res)?max<T>():res;
    }
    template<typename T>
    T MUL(T a,T b){
        T res;
        return __builtin_mul_overflow(a,b,&res)?max<T>():res;
    }
};
using namespace overflow;
struct mint{
    using u64=uint_fast64_t;
    u64 a;
    constexpr mint() :a(0){}
    constexpr mint(ll x) :a((x>=0)?(x%MOD):(x%MOD+MOD) ) {}

    inline constexpr mint operator+(const mint rhs)const noexcept{
        return mint(*this)+=rhs;
    }
    inline constexpr mint operator-(const mint rhs)const noexcept{
        return mint(*this)-=rhs;
    }
    inline constexpr mint operator*(const mint rhs)const noexcept{
        return mint(*this)*=rhs;
    }
    inline constexpr mint operator/(const mint rhs)const noexcept{
        return mint(*this)/=rhs;
    }
    inline constexpr mint operator+(const ll rhs) const noexcept{
        return mint(*this)+=mint(rhs);
    }
    inline constexpr mint operator-(const ll rhs)const noexcept{
        return mint(*this)-=mint(rhs);
    }
    inline constexpr mint operator*(const ll rhs)const noexcept{
        return mint(*this)*=mint(rhs);
    }
    inline constexpr mint operator/(const ll rhs)const noexcept{
        return mint(*this)/=mint(rhs);
    }

    inline constexpr mint &operator+=(const mint rhs)noexcept{
        a+=rhs.a;
        if(a>=MOD) a-=MOD;
        return *this;
    }
    inline constexpr mint &operator-=(const mint rhs)noexcept{
        if(rhs.a>a) a+=MOD;
        a-=rhs.a;
        return *this;
    }
    inline constexpr mint &operator*=(const mint rhs)noexcept{
        a=(a*rhs.a)%MOD;
        return *this;
    }
    inline constexpr mint &operator/=(mint rhs)noexcept{
        a=(a*rhs.inverse().a)%MOD;
        return *this;
    }
    inline constexpr mint &operator+=(const ll rhs)noexcept{
        return *this+=mint(rhs);
    }
    inline constexpr mint &operator-=(const ll rhs)noexcept{
        return *this-=mint(rhs);
    }
    inline constexpr mint &operator*=(const ll rhs)noexcept{
        return *this*=mint(rhs);
    }
    inline constexpr mint &operator/=(const ll rhs)noexcept{
        return *this/=mint(rhs);
    }

    inline constexpr mint operator=(const ll x)noexcept{
        a=(x>=0)?(x%MOD):(x%MOD+MOD);
        return *this;
    }

    inline constexpr bool operator==(const mint p)const noexcept{
        return a==p.a;
    }

    inline constexpr bool operator!=(const mint p)const noexcept{
        return a!=p.a;
    }

    inline constexpr mint pow(ll N) const noexcept{
        mint ans(1LL),p(a);
        while(N>0){
            if(bitUP(N,0)){
                ans*=p;
            }
            p*=p;
            N>>=1;
        }
        return ans;
    }
    inline constexpr mint inverse() const noexcept{
        return pow(MOD-2);
    }

};
inline constexpr mint operator+(const ll &a,const mint &b)noexcept{
    return mint(a)+=b;
}
inline constexpr mint operator-(const ll &a,const mint &b)noexcept{
    return mint(a)-=b;
}
inline constexpr mint operator*(const ll &a,const mint &b)noexcept{
    return mint(a)*=b;
}
inline constexpr mint operator/(const ll &a,const mint &b)noexcept{
    return mint(a)/=b;
}
//cout
inline ostream &operator<<(ostream &os,const mint &p) {
    return os<<p.a;
}

//cin
inline istream &operator>>(istream &is,mint &p) {
    ll t;
    is>>t;
    p=t;
    return is;
}

struct Binominal{
    vector<mint> fac,finv,inv; //fac[n]:n! finv:(n!)の逆元
    int sz;
    Binominal(int n=10) :sz(1){
        if(n<=0) n=10;
        init(n);
    }
    inline void init(int n){
        fac.resize(n+1,1);
        finv.resize(n+1,1);
        inv.resize(n+1,1);
        for(int i=sz+1;i<=n;i++){
            fac[i]=fac[i-1]*i;
            inv[i]=MOD-inv[MOD%i]*(MOD/i);
            finv[i]=finv[i-1]*inv[i];
        }
        sz=n;
    }
    //nCk(n,k<=N) をO(1)で求める
    inline mint com(int n,int k){
        if(n<k) return mint(0);
        if(n<0 || k<0) return mint(0);
        if(n>sz) init(n);
        return fac[n]*finv[k]*finv[n-k];
    }
    //nCk(k<=N) をO(k)　で求める 
    inline mint rcom(ll n,int k){
        if(n<0 || k<0 || n<k) return mint(0);
        if(k>sz) init(k);
        mint ret(1);
        for(int i=0;i<k;i++){
            ret*=n-i;
        }
        ret*=finv[k];
        return ret;
    }

    //重複組み合わせ n種類のものから重複を許してk個を選ぶ
    //〇がn個,|がk個
    inline mint h(int n,int k){
        return com(n+k-1,k);
    }
    //順列の公式
    inline mint P(int n,int k){
        if(n<k) return 0;
        if(n>sz) init(n);
        return fac[n]*finv[n-k];
    }

};
vector<int> Subset(int S,bool zero=false,bool full=false){
    vector<int> ret;
    int now=(S-1)&S;
    if(full and S){
        ret.push_back(S);
    }
    do{
        ret.push_back(now);
        now=(now-1)&S;
    }while(now!=0);
    if(zero){
        ret.push_back(0);
    }
    return ret;
}
template<typename T>
T SUM(const vector<T> &v,int s,int t){
    chmax(s,0);
    chmin(t,int(v.size())-1);
    if(s>t) return 0;
    if(s==0) return v[t];
    else return v[t]-v[s-1];
}
template<typename T>
void buildSUM(vector<T> &v){
    for(size_t i=1;i<v.size();i++){
        v[i]+=v[i-1];
    }
    return;
}

//erase_vertexの頂点を消して
//インデックスを調整したグラフを返す
Graph EraseVertex(Graph graph,vector<int> erase_vertex){
    vector<bool> erase(graph.size(),false);
    vector<int> sum(graph.size(),0);
    int cnt_erase=0;
    for(int idx:erase_vertex){
        if(erase[idx]) continue;
        cnt_erase++;
        erase[idx]=true;
        sum[idx]++;
    }
    for(int i=1;i<(int)sum.size();i++){
        sum[i]+=sum[i-1];
    }
    for(int i=0;i<int(graph.size())-cnt_erase;i++){
        int pre_i=i+sum[i];
        graph[i].clear();
        for(int to:graph[pre_i]){
            if(erase[to]) continue;
            graph[i].push_back(to-sum[to]);
        }
    }
    return graph;
}
//無限大(閉路がある)の場合、-1を返す
//O(V+E)
int LongestPath(const vector<vector<int>> &g){
    int sz=(int)g.size();
    vector<int> dp(sz,0);
    vector<int> cnt(sz,0);
    stack<int> st;
    for(int i=0;i<sz;i++){
        for(auto &e:g[i]){
            cnt[e]++;
        }
    }
    for(int i=0;i<sz;i++){
        if(cnt[i]==0) st.push(i);
    }
    int used=0;
    while(!st.empty()){
        int now=st.top(); st.pop();
        for(auto &e:g[now]){
            if(max(dp[e],dp[now]+1)){
                dp[e]=dp[now]+1;
            }
            cnt[e]--;
            if(cnt[e]==0) st.push(e);
        }
        used++;
    }
    if(used!=sz) return -1;
    int ret=0;
    for(int i=0;i<sz;i++){
        if(ret<dp[i]){
            ret=dp[i];
        }
    }
    return ret; 
}
string f(string c1){
    if(c1.size()==1){
        c1=char('A'-1)+c1;
    }
    return c1;
}
int g(string c1){
    return (c1[0]-char('A'-1))*26+c1[1];
}
#define D(a) D[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]][a[9]]
//           2,3,,5,,7,,11,13,15,,17,19
bool D[1100][5][3][2][2][2][2][2][2][2];

using A=array<int,10>;

void solve(){
    int N,M;
    cin>>N>>M;
    WeightGraph<int> G(N);
    for(int i=0;i<M;i++){
        int a,b,c; 
        cin>>a>>b>>c; 
        a--; b--;
        G[a].emplace_back(b,c);
    }
    vector<int> d={2,3,5,7,11,13,15,17,19};
    vector<vector<int>> g(21);
    for(int i=0;i<21;i++){
        for(int x:d){
            int cnt=0;
            int z=i;
            while(z and z%x==0){
                cnt++;
                z/=x;
            }
            g[i].push_back(cnt);
        }
    }
    queue<A> que;
    A a;
    for(int i=0;i<10;i++) a[i]=0;
    que.emplace(a);
    ll min_ans=max<ll>();
    ll max_ans=-1;
    while(que.size()){
        A now=que.front();
        if(now[0]==N-1){
            ll t=1;
            for(int i=0;i<d.size();i++){
                for(int j=0;j<now[i+1];j++) t*=d[i]; 
            }
            chmin(min_ans,t);
            chmax(max_ans,t);
        }
        que.pop();
        for(auto e:G[now[0]]){
            auto next=now;
            next[0]=e.to;
            for(int i=0;i<d.size();i++){
                chmax(next[i+1],g[e.cost][i]);
            }
            if(D(next)) continue;
            D(next)=true;
            que.push(next);
        }
    }
    if(max_ans==-1) cout<<"IMPOSSIBLE"<<endl;
    else cout<<min_ans<<" "<<max_ans<<endl;
}

int main(){
    bin101();
    int T=1;
    //cin>>T;
    while(T--) solve();
}
accuracy: 0.3170731707317073
time_ms: 10
memory_kb: 5164
final_rank: 17

code_snippet: #include<bits/stdc++.h> 
using namespace std;
using ll=long long int;
using Int=__int128;
#define ALL(A) A.begin(),A.end()
template<typename T1,typename T2> bool chmin(T1 &a,T2 b){if(a<=b)return 0; a=b; return 1;}
template<typename T1,typename T2> bool chmax(T1 &a,T2 b){if(a>=b)return 0; a=b; return 1;}
template<typename T> constexpr int bitUP(T x,int a){return (x>>a)&1;}
//→　↓　←　↑ 
int dh[4]={1,1,-1,-1}, dw[4]={1,-1,1,-1};
//右上から時計回り
//int dh[8]={-1,0,1,1,1,0,-1,-1}, dw[8]={1,1,1,0,-1,-1,-1,0};
long double EPS = 1e-6;
long double PI = acos(-1);
const ll INF=(1LL<<62);
const int MAX=(1<<30);
//constexpr ll MOD=1000000000+7;
constexpr ll MOD=998244353;


inline void bin101(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(20);
}

using pii=pair<int,int>;
using pil=pair<int,ll>;
using pli=pair<ll,int>;
using pll=pair<ll,ll>;
using psi=pair<string,int>;
using pis=pair<int,string>;
using psl=pair<string,ll>;
using pls=pair<ll,string>;
using pss=pair<string,string>;

using Graph=vector<vector<int>>;

template<typename T >
struct edge {
	int to;
	T cost;
	edge()=default;
	edge(int to, T cost) : to(to), cost(cost) {}

};
template<typename T>
using WeightGraph=vector<vector<edge<T>>>;

template<typename T>
void CinGraph(int M,WeightGraph<T> &g,bool directed=false,bool index1=true){
    while(M--){
        int s,t;
        T cost;
        cin>>s>>t>>cost;
        if(index1) s--,t--;
        g[s].emplace_back(t,cost);
        if(not directed) g[t].emplace_back(s,cost);
    }
}

void CinGraph(int M,Graph &g,bool directed=false,bool index1=true){
    while(M--){
        int s,t;
        cin>>s>>t;
        if(index1) s--,t--;
        g[s].push_back(t);
        if(not directed) g[t].push_back(s);
    }
}


//0-indexed vector cin
template<typename T>
inline istream &operator>>(istream &is,vector<T> &v) {
    for(size_t i=0;i<v.size();i++) is>>v[i];
	return is;
}
 
//0-indexed vector cin
template<typename T>
inline istream &operator>>(istream &is,vector<vector<T>> &v) {
    for(size_t i=0;i<v.size();i++){
        is>>v[i];
    }
    return is;
}
//vector cout
template<typename T>
inline ostream &operator<<(ostream &os,const vector<T> &v) {
    bool sp=true;
    if(string(typeid(T).name())=="c") sp=false;
    for(size_t i=0;i<v.size();i++){
        if(i and sp) os<<" ";
        os<<v[i];
    }
    return os;
}
//vector<vector> cout
template<typename T>
inline ostream &operator<<(ostream &os,const vector<vector<T>> &v) {
    for(size_t i=0;i<v.size();i++){
        os<<v[i];
        if(i+1!=v.size()) os<<"\n";
    }
    return os;
}

//Graph out
template<typename T>
inline ostream &operator<<(ostream &os,const Graph &g) {
    for(size_t i=0;i<g.size();i++){
        for(int to:g[i]){
            os<<i<<"->"<<to<<" ";
        }
        os<<endl;
    }
    return os;
}

//WeightGraph out
template<typename T>
inline ostream &operator<<(ostream &os,const WeightGraph<T> &g) {
    for(size_t i=0;i<g.size();i++){
        for(auto e:g[i]){
            os<<i<<"->"<<e.to<<"("<<e.cost<<") ";
        }
        os<<endl;
    }
    return os;
}


//要素数n 初期値x
template<typename T>
inline vector<T> vmake(size_t n,T x){
	return vector<T>(n,x);
}

//a,b,c,x data[a][b][c] 初期値x
template<typename... Args>
auto vmake(size_t n,Args... args){
	auto v=vmake(args...);
	return vector<decltype(v)>(n,move(v));
}
template<typename V,typename T>
void fill(V &v,const T value){
    v=value;
}

template<typename V,typename T>
void fill(vector<V> &vec,const T value){
    for(auto &v:vec) fill(v,value);
}

//pair cout
template<typename T, typename U>
inline ostream &operator<<(ostream &os,const pair<T,U> &p) {
	os<<p.first<<" "<<p.second;
	return os;
}
 
//pair cin
template<typename T, typename U>
inline istream &operator>>(istream &is,pair<T,U> &p) {
	is>>p.first>>p.second;
	return is;
}
 
//ソート
template<typename T>
inline void vsort(vector<T> &v){
    sort(v.begin(),v.end());
}
 
//逆順ソート
template<typename T>
inline void rvsort(vector<T> &v){
	sort(v.rbegin(),v.rend());
}

//1ビットの数を返す
inline int popcount(int x){
	return __builtin_popcount(x);
}
//1ビットの数を返す
inline int popcount(ll x){
	return __builtin_popcountll(x);
}
template<typename T>
inline void Compress(vector<T> &C){
    sort(C.begin(),C.end());
    C.erase(unique(C.begin(),C.end()),C.end());
}
template<typename T>
inline int lower_idx(const vector<T> &C,T value){
    return lower_bound(C.begin(),C.end(),value)-C.begin();
}
template<typename T>
inline int upper_idx(const vector<T> &C,T value){
    return upper_bound(C.begin(),C.end(),value)-C.begin();
}
//時計回りに90度回転
template<typename T>
inline void rotate90(vector<vector<T>> &C){
    vector<vector<T>> D(C[0].size(),vector<T>(C.size()));
    for(int h=0;h<C.size();h++){
        for(int w=0;w<C[h].size();w++){
            D[w][C.size()-1-h]=C[h][w];
        }
    }
    C=D;
}
//補グラフを返す
//i→iのような辺は加えない
Graph ComplementGraph(const Graph &g){
    size_t sz=g.size();
    bool used[sz][sz];
    fill(used[0],used[sz],false);
    for(size_t i=0;i<sz;i++){
        for(int to:g[i]){
            used[i][to]=true;
        }
    }
    Graph ret(sz);
    for(size_t i=0;i<sz;i++){
        for(size_t j=0;j<sz;j++){
            if(used[i][j]) continue;
            if(i==j) continue;
            ret[i].push_back(j);
        }
    }
    return ret;
}
//グラフの分解 secondはある頂点がどこに対応するか id[i]={2,3}のとき,頂点iはret[2][3]に対応
//無効グラフのみに対応
pair<vector<Graph>,vector<pair<int,int>>> GraphDecomposition(const Graph &g){
    vector<pair<int,int>> id(g.size(),pair<int,int>(-1,-1));
    vector<Graph> ret;
    vector<int> now;
    for(size_t i=0;i<g.size();i++){
        if(id[i].first!=-1) continue;
        id[i].first=ret.size();
        id[i].second=0;
        now.push_back(i);
        for(size_t j=0;j<now.size();j++){
            for(int to:g[now[j]]){
                if(id[to].first==-1){
                    id[to].first=ret.size();
                    id[to].second=now.size();
                    now.push_back(to);
                }
            }
        }
        Graph r(now.size());
        for(size_t j=0;j<now.size();j++){
            r[j]=g[now[j]];
            for(int &to:r[j]){
                to=id[to].second;
            }
        }
        ret.push_back(r);
        now.clear();
    }
    return make_pair(ret,id);
}
//0indexを想定
bool OutGrid(ll h,ll w,ll H,ll W){
    return (h>=H or w>=W or h<0 or w<0);
}

void NO(){
    cout<<"NO"<<"\n";
}
void YES(){
    cout<<"YES"<<"\n";
}
void No(){
    cout<<"No"<<"\n";
}
void Yes(){
    cout<<"Yes"<<"\n";
}
namespace overflow{
    template<typename T>
    T max(){
        return numeric_limits<T>::max();
    }
    template<typename T>
    T ADD(T a,T b){
        T res;
        return __builtin_add_overflow(a,b,&res)?max<T>():res;
    }
    template<typename T>
    T MUL(T a,T b){
        T res;
        return __builtin_mul_overflow(a,b,&res)?max<T>():res;
    }
};
using namespace overflow;
struct mint{
    using u64=uint_fast64_t;
    u64 a;
    constexpr mint() :a(0){}
    constexpr mint(ll x) :a((x>=0)?(x%MOD):(x%MOD+MOD) ) {}

    inline constexpr mint operator+(const mint rhs)const noexcept{
        return mint(*this)+=rhs;
    }
    inline constexpr mint operator-(const mint rhs)const noexcept{
        return mint(*this)-=rhs;
    }
    inline constexpr mint operator*(const mint rhs)const noexcept{
        return mint(*this)*=rhs;
    }
    inline constexpr mint operator/(const mint rhs)const noexcept{
        return mint(*this)/=rhs;
    }
    inline constexpr mint operator+(const ll rhs) const noexcept{
        return mint(*this)+=mint(rhs);
    }
    inline constexpr mint operator-(const ll rhs)const noexcept{
        return mint(*this)-=mint(rhs);
    }
    inline constexpr mint operator*(const ll rhs)const noexcept{
        return mint(*this)*=mint(rhs);
    }
    inline constexpr mint operator/(const ll rhs)const noexcept{
        return mint(*this)/=mint(rhs);
    }

    inline constexpr mint &operator+=(const mint rhs)noexcept{
        a+=rhs.a;
        if(a>=MOD) a-=MOD;
        return *this;
    }
    inline constexpr mint &operator-=(const mint rhs)noexcept{
        if(rhs.a>a) a+=MOD;
        a-=rhs.a;
        return *this;
    }
    inline constexpr mint &operator*=(const mint rhs)noexcept{
        a=(a*rhs.a)%MOD;
        return *this;
    }
    inline constexpr mint &operator/=(mint rhs)noexcept{
        a=(a*rhs.inverse().a)%MOD;
        return *this;
    }
    inline constexpr mint &operator+=(const ll rhs)noexcept{
        return *this+=mint(rhs);
    }
    inline constexpr mint &operator-=(const ll rhs)noexcept{
        return *this-=mint(rhs);
    }
    inline constexpr mint &operator*=(const ll rhs)noexcept{
        return *this*=mint(rhs);
    }
    inline constexpr mint &operator/=(const ll rhs)noexcept{
        return *this/=mint(rhs);
    }

    inline constexpr mint operator=(const ll x)noexcept{
        a=(x>=0)?(x%MOD):(x%MOD+MOD);
        return *this;
    }

    inline constexpr bool operator==(const mint p)const noexcept{
        return a==p.a;
    }

    inline constexpr bool operator!=(const mint p)const noexcept{
        return a!=p.a;
    }

    inline constexpr mint pow(ll N) const noexcept{
        mint ans(1LL),p(a);
        while(N>0){
            if(bitUP(N,0)){
                ans*=p;
            }
            p*=p;
            N>>=1;
        }
        return ans;
    }
    inline constexpr mint inverse() const noexcept{
        return pow(MOD-2);
    }

};
inline constexpr mint operator+(const ll &a,const mint &b)noexcept{
    return mint(a)+=b;
}
inline constexpr mint operator-(const ll &a,const mint &b)noexcept{
    return mint(a)-=b;
}
inline constexpr mint operator*(const ll &a,const mint &b)noexcept{
    return mint(a)*=b;
}
inline constexpr mint operator/(const ll &a,const mint &b)noexcept{
    return mint(a)/=b;
}
//cout
inline ostream &operator<<(ostream &os,const mint &p) {
    return os<<p.a;
}

//cin
inline istream &operator>>(istream &is,mint &p) {
    ll t;
    is>>t;
    p=t;
    return is;
}

struct Binominal{
    vector<mint> fac,finv,inv; //fac[n]:n! finv:(n!)の逆元
    int sz;
    Binominal(int n=10) :sz(1){
        if(n<=0) n=10;
        init(n);
    }
    inline void init(int n){
        fac.resize(n+1,1);
        finv.resize(n+1,1);
        inv.resize(n+1,1);
        for(int i=sz+1;i<=n;i++){
            fac[i]=fac[i-1]*i;
            inv[i]=MOD-inv[MOD%i]*(MOD/i);
            finv[i]=finv[i-1]*inv[i];
        }
        sz=n;
    }
    //nCk(n,k<=N) をO(1)で求める
    inline mint com(int n,int k){
        if(n<k) return mint(0);
        if(n<0 || k<0) return mint(0);
        if(n>sz) init(n);
        return fac[n]*finv[k]*finv[n-k];
    }
    //nCk(k<=N) をO(k)　で求める 
    inline mint rcom(ll n,int k){
        if(n<0 || k<0 || n<k) return mint(0);
        if(k>sz) init(k);
        mint ret(1);
        for(int i=0;i<k;i++){
            ret*=n-i;
        }
        ret*=finv[k];
        return ret;
    }

    //重複組み合わせ n種類のものから重複を許してk個を選ぶ
    //〇がn個,|がk個
    inline mint h(int n,int k){
        return com(n+k-1,k);
    }
    //順列の公式
    inline mint P(int n,int k){
        if(n<k) return 0;
        if(n>sz) init(n);
        return fac[n]*finv[n-k];
    }

};
vector<int> Subset(int S,bool zero=false,bool full=false){
    vector<int> ret;
    int now=(S-1)&S;
    if(full and S){
        ret.push_back(S);
    }
    do{
        ret.push_back(now);
        now=(now-1)&S;
    }while(now!=0);
    if(zero){
        ret.push_back(0);
    }
    return ret;
}
template<typename T>
T SUM(const vector<T> &v,int s,int t){
    chmax(s,0);
    chmin(t,int(v.size())-1);
    if(s>t) return 0;
    if(s==0) return v[t];
    else return v[t]-v[s-1];
}
template<typename T>
void buildSUM(vector<T> &v){
    for(size_t i=1;i<v.size();i++){
        v[i]+=v[i-1];
    }
    return;
}

//erase_vertexの頂点を消して
//インデックスを調整したグラフを返す
Graph EraseVertex(Graph graph,vector<int> erase_vertex){
    vector<bool> erase(graph.size(),false);
    vector<int> sum(graph.size(),0);
    int cnt_erase=0;
    for(int idx:erase_vertex){
        if(erase[idx]) continue;
        cnt_erase++;
        erase[idx]=true;
        sum[idx]++;
    }
    for(int i=1;i<(int)sum.size();i++){
        sum[i]+=sum[i-1];
    }
    for(int i=0;i<int(graph.size())-cnt_erase;i++){
        int pre_i=i+sum[i];
        graph[i].clear();
        for(int to:graph[pre_i]){
            if(erase[to]) continue;
            graph[i].push_back(to-sum[to]);
        }
    }
    return graph;
}
//無限大(閉路がある)の場合、-1を返す
//O(V+E)
int LongestPath(const vector<vector<int>> &g){
    int sz=(int)g.size();
    vector<int> dp(sz,0);
    vector<int> cnt(sz,0);
    stack<int> st;
    for(int i=0;i<sz;i++){
        for(auto &e:g[i]){
            cnt[e]++;
        }
    }
    for(int i=0;i<sz;i++){
        if(cnt[i]==0) st.push(i);
    }
    int used=0;
    while(!st.empty()){
        int now=st.top(); st.pop();
        for(auto &e:g[now]){
            if(max(dp[e],dp[now]+1)){
                dp[e]=dp[now]+1;
            }
            cnt[e]--;
            if(cnt[e]==0) st.push(e);
        }
        used++;
    }
    if(used!=sz) return -1;
    int ret=0;
    for(int i=0;i<sz;i++){
        if(ret<dp[i]){
            ret=dp[i];
        }
    }
    return ret; 
}
string f(string c1){
    if(c1.size()==1){
        c1=char('A'-1)+c1;
    }
    return c1;
}
int g(string c1){
    return (c1[0]-char('A'-1))*26+c1[1];
}
#define D(a) D[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]][a[9]]
//           2,3,,5,,7,,11,13,15,,17,19
bool D[1100][5][3][2][2][2][2][2][2][2];

using A=array<int,10>;

void solve(){
    int N,M;
    cin>>N>>M;
    WeightGraph<int> G(N);
    for(int i=0;i<M;i++){
        int a,b,c; 
        cin>>a>>b>>c; 
        a--; b--;
        G[a].emplace_back(b,c);
    }
    vector<int> d={2,3,5,7,11,13,15,17,19};
    vector<vector<int>> g(21);
    for(int i=0;i<21;i++){
        for(int x:d){
            int cnt=0;
            int z=i;
            while(z and z%x==0){
                cnt++;
                z/=x;
            }
            g[i].push_back(cnt);
        }
    }
    queue<A> que;
    A a;
    for(int i=0;i<10;i++) a[i]=0;
    que.emplace(a);
    ll min_ans=INF;
    ll max_ans=-1;
    while(que.size()){
        A now=que.front();
        if(now[0]==N-1){
            ll t=1;
            for(int i=0;i<d.size();i++){
                for(int j=0;j<now[i+1];j++) t*=d[i]; 
            }
            chmin(min_ans,t);
            chmax(max_ans,t);
        }
        que.pop();
        for(auto e:G[now[0]]){
            auto next=now;
            next[0]=e.to;
            for(int i=0;i<d.size();i++){
                chmax(next[i+1],g[e.cost][i]);
            }
            if(D(next)) continue;
            D(next)=true;
            que.push(next);
        }
    }
    if(max_ans==-1) cout<<"IMPOSSIBLE"<<endl;
    else cout<<min_ans<<" "<<max_ans<<endl;
}

int main(){
    bin101();
    int T=1;
    //cin>>T;
    while(T--) solve();
}
accuracy: 0.3170731707317073
time_ms: 10
memory_kb: 5008
final_rank: 16