#include using namespace std; typedef long long ll; // 十分大きな値(ここでは 1e18) const ll INF = 1000000000000000000LL; // fast modular exponentiation: (base^exp) mod mod を返す int modExp(int base, int exp, int mod) { ll result = 1; ll b = base % mod; while(exp > 0){ if(exp & 1) result = (result * b) % mod; b = (b * b) % mod; exp >>= 1; } return (int)result; } // T0 を計算する関数 // T0 は「t! が N で割り切れる(すなわち,各素 p について // sum_{i>=1} floor(t/p^i) >= v_p(N) となる t)の中で最小の t」 int computeT0(int N) { int temp = N; vector> factors; // 試し割りで N の素因数分解(N ≤ 2e5 なので十分高速) for (int p = 2; p*p <= temp; p++){ if(temp % p == 0){ int cnt = 0; while(temp % p == 0){ cnt++; temp /= p; } factors.push_back({p, cnt}); } } if(temp > 1) factors.push_back({temp, 1}); int T0 = 1; // 各素因数 p^a について,最小の t を求める. for(auto &pr : factors){ int p = pr.first, cnt = pr.second; int lo = 1, hi = N; // t は N 以下で十分 int best = hi; while(lo <= hi){ int mid = (lo + hi) / 2; ll sum = 0; ll power = p; while(power <= mid){ sum += mid / power; if(power > mid / p) break; power *= p; } if(sum >= cnt){ best = mid; hi = mid - 1; } else { lo = mid + 1; } } T0 = max(T0, best); } return T0; } // 状態の定義 // phase == 0 : 実値 X(X < T0)を状態として持つ(フェーズ1) // phase == 1 : X の剰余 r = X mod N を状態として持つ(フェーズ2;X ≥ T0 と仮定) struct State { ll cost; int phase; // 0: phase1, 1: phase2 int val; // phase1 の場合は X, phase2 の場合は r (0 <= r < N) bool operator>(const State &other) const { return cost > other.cost; } }; // メイン int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int N, A, B, C; cin >> N >> A >> B >> C; // 制約より N, A, B, C は 2 以上 // T0 を計算:フェーズ1では X < T0 の状態を扱う int T0 = computeT0(N); // (T0 は必ず N 以下になります) // 【前処理】フェーズ1で使うために,1 ≤ x < T0 に対する x! mod N を計算 vector fact_mod(T0, 0); // 1-indexed で扱う(index 0 は使わない) fact_mod[1] = 1 % N; for (int i = 2; i < T0; i++){ fact_mod[i] = (int)(((ll)fact_mod[i-1] * i) % N); } // また,x! の値が T0 未満かどうかを判定するため, // 実際の x! (ただし T0 未満なら正確な値,T0 以上なら T0 とする) を precompute vector fact_exact(T0, 0); fact_exact[1] = 1; for (int i = 2; i < T0; i++){ // もし fact_exact[i-1] * i < T0 なら正確な値, // そうでなければ T0 以上とみなして T0 をセット if(fact_exact[i-1] > (T0 - 1LL) / i) fact_exact[i] = T0; else fact_exact[i] = fact_exact[i-1] * i; } // dp1[x]: フェーズ1 (実値状態) で X = x のときの最小コスト (1 ≤ x < T0) // dp2[r]: フェーズ2 (剰余状態) で X mod N = r のときの最小コスト vector dp1(T0, INF); vector dp2(N, INF); // 優先度付きキュー(Dijkstra用) priority_queue, greater> pq; // 初期状態:X = 1 (フェーズ1) dp1[1] = 0; pq.push({0, 0, 1}); while(!pq.empty()){ State cur = pq.top(); pq.pop(); ll curCost = cur.cost; int phase = cur.phase; int curVal = cur.val; if(phase == 0){ // フェーズ1:状態 X (1 ≤ X < T0) if(curCost != dp1[curVal]) continue; // 更新済みなら飛ばす // 【操作1】+1: X -> X+1 (コスト A) int newX = curVal + 1; ll newCost = curCost + A; if(newX < T0){ if(newCost < dp1[newX]){ dp1[newX] = newCost; pq.push({newCost, 0, newX}); } } else { int r = newX % N; if(newCost < dp2[r]){ dp2[r] = newCost; pq.push({newCost, 1, r}); } } // 【操作2】累乗: X -> X^k, k ≥ 2, コストは B^k // ※ X=1 のときは 1^k = 1 なので効果がないのでスキップ if(curVal != 1){ ll power = curVal; // 現在は X^1 ll costPow = B; // このループで k=2 から始めるため、最初 costPow に B を掛けて B^2 とする for (int k = 2; ; k++){ costPow *= B; // 現在 costPow = B^k if(curCost > INF - costPow) break; // オーバーフロー防止 ll nextCost = curCost + costPow; // X^k を計算する際,もし power * X < T0 ならフェーズ1内で遷移可能 if(power <= (T0 - 1LL) / curVal){ ll newVal = power * curVal; if(newVal < T0){ if(nextCost < dp1[newVal]){ dp1[newVal] = nextCost; pq.push({nextCost, 0, (int)newVal}); } power = newVal; // 次の k で使う if(costPow > INF / B) break; continue; } else { // newVal >= T0 ならフェーズ2へ遷移(実際の値は捨て,剰余 mod N を記録) int r = modExp(curVal, k, N); if(nextCost < dp2[r]){ dp2[r] = nextCost; pq.push({nextCost, 1, r}); } break; } } else { // これ以上フェーズ1内で計算できないので,フェーズ2へ int r = modExp(curVal, k, N); if(nextCost < dp2[r]){ dp2[r] = nextCost; pq.push({nextCost, 1, r}); } break; } if(costPow > INF / B) break; } } // 【操作3】階乗: X -> X! (コスト C) // 事前計算した fact_exact, fact_mod を利用する. ll fval = fact_exact[curVal]; ll newCostF = curCost + C; if(fval < T0){ int newX = (int) fval; if(newCostF < dp1[newX]){ dp1[newX] = newCostF; pq.push({newCostF, 0, newX}); } } else { int r = fact_mod[curVal]; if(newCostF < dp2[r]){ dp2[r] = newCostF; pq.push({newCostF, 1, r}); } } } else { // フェーズ2:状態は剰余 r (0 ≤ r < N),ここでは X ≥ T0 と仮定 if(curCost != dp2[curVal]) continue; // 既に r == 0 なら X は N の倍数 if(curVal == 0){ cout << curCost << "\n"; return 0; } // 【操作1】+1: 剰余 r -> (r+1) mod N (コスト A) int new_r = (curVal + 1) % N; ll newCost = curCost + A; if(newCost < dp2[new_r]){ dp2[new_r] = newCost; pq.push({newCost, 1, new_r}); } // 【操作2】累乗: X -> X^k と同様に,剰余も r -> r^k mod N (コスト B^k) // ※ r=0,1 のときは変化しないので対象外 if(curVal != 0 && curVal != 1){ ll expCost = B; // k=2 から開始するため for (int k = 2; ; k++){ expCost *= B; // expCost = B^k if(curCost > INF - expCost) break; ll nextCost = curCost + expCost; int r_new = modExp(curVal, k, N); if(nextCost < dp2[r_new]){ dp2[r_new] = nextCost; pq.push({nextCost, 1, r_new}); } // 安全のため、k の上限を適当に設定 if(k >= 60) break; if(expCost > INF / B) break; } } // 【操作3】階乗: X! (コスト C) // X が十分大きい(X ≥ T0)なら X! は必ず N で割り切るので, // 階乗操作で直接解 (剰余 0) となる ll nextCost = curCost + C; if(nextCost < dp2[0]){ dp2[0] = nextCost; pq.push({nextCost, 1, 0}); } } } // 答えは dp2[0] に入っているはず cout << dp2[0] << "\n"; return 0; }