#include using namespace std; // [floor_sqrt(N)] // 0 <= N <= 9e18 long long floor_sqrt(long long N) { long long ub = 3'000'000'001; long long lb = 0; while (ub - lb > 1) { long long t = (ub + lb) / 2; if (t * t <= N) lb = t; else ub = t; } return lb; } // Quotients struct Quotients { long long sqrtN; long long N; long long M; std::vector data; Quotients(long long _N): N(_N) { sqrtN = floor_sqrt(N); data = std::vector(sqrtN); for (int i=1; i<=sqrtN; i++) { data[i-1] = i; } for (int i=sqrtN-1; i>=0; i--){ if (N / data[i] == data[i]) continue; data.emplace_back(N / data[i]); } M = data.size(); } int size() { return M; } long long operator[](int i) { if (i < 0 || i >= M){ throw std::out_of_range("Index out of range"); } return data[i]; } int ind(long long x) { if (x <= sqrtN) return x - 1; return M - N / x; } std::pair interval(long long v) { return std::pair(N / (v+1) + 1, N / v + 1); } }; // Lucy DP // returns (dp, prime_count) template std::pair, std::vector> lucy_dp(long long N) { Quotients Q(N); int m = Q.size(); std::vector dp(m); std::vector dp2(m); for (int i=0; i dp2[x-2]) { long long border = (long long)x * x; for (int i=m-1; i>=0; i--){ if (Q[i] < border) break; int tmpind = Q.ind(Q[i]/x); dp[i] -= g(x) * (dp[tmpind] - dp[x-2]); dp2[i] -= dp2[tmpind] - dp2[x-2]; } } } return std::pair(dp, dp2); } // enumerate primes <= N std::vector enumerate_prime(long long N) { std::vector p(N+1); std::vector ret(0); for (long long i=2; i<=N; i++){ if (p[i]) continue; ret.emplace_back(i); for (long long j=2*i; j<=N; j+=i){ p[j] = true; } } return ret; } // black algorithm // recommended for N <= 1e12 template T black_algorithm( std::vector GF, long long N ) { if (N == 1) return 1; Quotients Q(N); std::vector prime_list = enumerate_prime(Q.sqrtN); int pm = prime_list.size(); auto dfs = [&]( auto self, long long num, T f_old, long long gpf, long long c, long long num_tamari ) -> T { T ret = 0; if (gpf >= 0 && num * prime_list[gpf] <= N) { ret += f_old * f(prime_list[gpf], c+1, num_tamari * prime_list[gpf]); if (num * prime_list[gpf] * prime_list[gpf] <= N){ ret += self(self, num * prime_list[gpf], f_old, gpf, c + 1, num_tamari * prime_list[gpf] ); } } if (gpf >= 0){ int lft = Q.ind(prime_list[gpf]); int rgt = Q.ind(N / num); if (lft < rgt){ ret += f_old * f(prime_list[gpf], c, num_tamari) * (GF[rgt] - GF[lft]); } }else{ int lft = 0; int rgt = Q.ind(N / num); if (lft < rgt){ ret += GF[rgt] - GF[lft]; } } for (long long i=gpf+1; i= 0){ ret += self(self, num * prime_list[i], f_old * f(prime_list[gpf], c, num_tamari), i, 1, prime_list[i] ); }else{ ret += self(self, num * prime_list[i], f_old, i, 1, prime_list[i] ); } }else{ break; } } return ret; }; return dfs(dfs, 1, 1, -1, 0, 1) + T(1); } #include typedef atcoder::modint998244353 mint; typedef long long ll; mint t[60]; mint f(ll p, ll e, ll n) { return t[e+1]; } mint g(ll p) { return 1; } mint G(ll p){ return p; } int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL); ll n, m; cin >> n >> m; for (int i=1; i<60; i++){ t[i] = mint(i).pow(n); } auto [a,b] = lucy_dp(m); vector x((int)a.size()); mint tmp = mint(2).pow(n); for (int i=0; i<(int)a.size(); i++){ x[i] = a[i] * tmp; } mint ans = black_algorithm(x,m); cout << ans.val() << '\n'; }