// #pragma GCC optimize("O3,unroll-loops") #include using namespace std; #include using namespace atcoder; // #include // using namespace boost::multiprecision; #define ll long long #define ld long double #define rep(i, n) for (ll i = 0; i < (ll)(n); ++i) #define vi vector #define vl vector #define vd vector #define vb vector #define vs vector #define vc vector #define ull unsigned long long #define all(a) (a).begin(), (a).end() #define rall(a) (a).rbegin(), (a).rend() template inline bool chmax(T &a, const U &b) { if (a < b) { a = b; return true; } return false; } template inline bool chmin(T &a, const U &b) { if (a > b) { a = b; return true; } return false; } // #define ll int // #define ll int128_t // #define ll int256_t // #define ll cpp_int constexpr ll inf = (1ll << 61); // constexpr ll inf = (1 << 30); // const double PI=3.1415926535897932384626433832795028841971; uint32_t xor_x = 123456789, xor_y = 362436069, xor_z = 521288629, xor_w = 88675123; inline uint32_t xor_next() { uint32_t t = xor_x ^ (xor_x << 11); xor_x = xor_y; xor_y = xor_z; xor_z = xor_w; return xor_w = (xor_w ^ (xor_w >> 19)) ^ (t ^ (t >> 8)); } inline int rnd(int max_val) { return xor_next() % max_val; } // ll rui(ll a,ll b){ // if(b==0)return 1; // if(b%2==1) return a*rui(a*a,b/2); // return rui(a*a,b/2); // } // vl fact; // ll kai(ll n){ // fact.resize(n,1); // rep(i,n-1)fact[i+1]=fact[i]*(i+1); // } // using mint = ld; using mint = modint998244353;//static_modint<998244353> // using mint = modint1000000007;//static_modint<1000000007> // using mint = static_modint<922267487>; // 多分落とされにくい NOT ntt-friendly // using mint = static_modint<469762049>; // ntt-friendly // using mint = static_modint<167772161>; // ntt-friendly // using mint = modint;//mint::set_mod(mod); // ll const mod=1000000007ll; // ll const mod=998244353ll; // ll modrui(ll a,ll b,ll mod){ // a%=mod; // if(b==0)return 1; // if(b%2==1) return a*modrui(a*a%mod,b/2,mod)%mod; // return modrui(a*a%mod,b/2,mod)%mod; // } // void incr(vl &v,ll n){// n進法 // ll k=v.size(); // v[k-1]++; // ll now=k-1; // while (v[now]>=n) // { // v[now]=0; // if(now==0)break; // v[now-1]++; // now--; // } // return; // } vector fact,invf; void init_modfact(ll sz){ fact.resize(sz); invf.resize(sz); fact[0]=1; rep(i,sz-1){ fact[i+1]=fact[i]*(i+1); } invf[sz-1]=1/fact[sz-1]; for(ll i=sz-2; i>=0; i--){ invf[i]=invf[i+1]*(i+1); } } mint choose(ll n,ll r){ if(n modpow,invpow; // void init_modpow(ll x,ll sz){ // mint inv=1/mint(x); // modpow.assign(sz,1); // invpow.assign(sz,1); // rep(i,sz-1){ // modpow[i+1]=modpow[i]*x; // invpow[i+1]=invpow[i]*inv; // } // } // long long phi(long long n) {// O(sqrt(n)) // long long res = n; // for (long long i = 2; i * i <= n; i++) { // if (n % i == 0) { // res -= res / i; // while (n % i == 0) n /= i; // } // } // if (n > 1) res -= res / n; // return res; // } // 警告の抑制 #define _CRT_SECURE_NO_WARNINGS // ライブラリの読み込み #include using namespace std; // 型名の短縮 using pii = pair; using pll = pair; using pil = pair; using pli = pair; using vvi = vector; using vvvi = vector; using vvvvi = vector; using vvl = vector; using vvvl = vector; using vvvvl = vector; using vvb = vector; using vvvb = vector; using vvc = vector; using vvvc = vector; using vvd = vector; using vvvd = vector; template using priority_queue_rev = priority_queue, greater>; using Graph = vvi; // 定数の定義 const double PI = acos(-1); int DX[4] = { 1, 0, -1, 0 }; // 4 近傍(下,右,上,左) int DY[4] = { 0, 1, 0, -1 }; int INF = 1001001001; ll INFL = 4004004003094073385LL; // (int)INFL = INF, (int)(-INFL) = -INF; // 入出力高速化 struct fast_io { fast_io() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(18); } } fastIOtmp; // 汎用マクロの定義 #define all(a) (a).begin(), (a).end() #define sz(x) ((int)(x).size()) #define lbpos(a, x) (int)distance((a).begin(), std::lower_bound(all(a), (x))) #define ubpos(a, x) (int)distance((a).begin(), std::upper_bound(all(a), (x))) #define Yes(b) {cout << ((b) ? "Yes\n" : "No\n");} #define repi(i, s, t) for(int i = int(s), i##_end = int(t); i <= i##_end; ++i) // s から t まで昇順 #define repir(i, s, t) for(int i = int(s), i##_end = int(t); i >= i##_end; --i) // s から t まで降順 #define repe(v, a) for(const auto& v : (a)) // a の全要素(変更不可能) #define repea(v, a) for(auto& v : (a)) // a の全要素(変更可能) #define repb(set, d) for(int set = 0, set##_ub = 1 << int(d); set < set##_ub; ++set) // d ビット全探索(昇順) #define repis(i, set) for(int i = lsb(set), bset##i = set; i < 32; bset##i -= 1 << i, i = lsb(bset##i)) // set の全要素(昇順) #define repp(a) sort(all(a)); for(bool a##_perm = true; a##_perm; a##_perm = next_permutation(all(a))) // a の順列全て(昇順) #define uniq(a) {sort(all(a)); (a).erase(unique(all(a)), (a).end());} // 重複除去 #define EXIT(a) {cout << (a) << endl; exit(0);} // 強制終了 #define inQ(x, y, u, l, d, r) ((u) <= (x) && (l) <= (y) && (x) < (d) && (y) < (r)) // 半開矩形内判定 // 汎用関数の定義 template inline ll powi(T n, int k) { ll v = 1; rep(i, k) v *= n; return v; } template inline bool chmax(T& M, const T& x) { if (M < x) { M = x; return true; } return false; } // 最大値を更新(更新されたら true を返す) template inline bool chmin(T& m, const T& x) { if (m > x) { m = x; return true; } return false; } // 最小値を更新(更新されたら true を返す) template inline T getb(T set, int i) { return (set >> i) & T(1); } template inline T smod(T n, T m) { n %= m; if (n < 0) n += m; return n; } // 非負mod // 演算子オーバーロード template inline istream& operator>>(istream& is, pair& p) { is >> p.first >> p.second; return is; } template inline istream& operator>>(istream& is, vector& v) { repea(x, v) is >> x; return is; } template inline vector& operator--(vector& v) { repea(x, v) --x; return v; } template inline vector& operator++(vector& v) { repea(x, v) ++x; return v; } #if __has_include() #include using namespace atcoder; #ifdef _MSC_VER #include "localACL.hpp" #endif namespace atcoder { inline istream& operator>>(istream& is, mint& x) { ll x_; is >> x_; x = x_; return is; } inline ostream& operator<<(ostream& os, const mint& x) { os << x.val(); return os; } } using vm = vector; using vvm = vector; using vvvm = vector; using vvvvm = vector; using pim = pair; #endif #ifdef _MSC_VER // 手元環境(Visual Studio) #include "local.hpp" #else // 提出用(gcc) inline int popcount(int n) { return __builtin_popcount(n); } inline int popcount(ll n) { return __builtin_popcountll(n); } inline int lsb(int n) { return n != 0 ? __builtin_ctz(n) : 32; } inline int lsb(ll n) { return n != 0 ? __builtin_ctzll(n) : 64; } template inline int lsb(const bitset& b) { return b._Find_first(); } inline int msb(int n) { return n != 0 ? (31 - __builtin_clz(n)) : -1; } inline int msb(ll n) { return n != 0 ? (63 - __builtin_clzll(n)) : -1; } #define dump(...) #define dumpel(...) #define dump_list(v) #define dump_mat(v) #define input_from_file(f) #define output_to_file(f) #define Assert(b) { if (!(b)) { vc MLE(1<<30); EXIT(MLE.back()); } } // RE の代わりに MLE を出す #endif //【階乗など(法が大きな素数)】 /* * Factorial_mint(int N) : O(n) * N まで計算可能として初期化する. * * mint fact(int n) : O(1) * n! を返す. * * mint fact_inv(int n) : O(1) * 1/n! を返す(n が負なら 0 を返す) * * mint inv(int n) : O(1) * 1/n を返す. * * mint perm(int n, int r) : O(1) * 順列の数 nPr を返す. * * mint bin(int n, int r) : O(1) * 二項係数 nCr を返す. * * mint bin_inv(int n, int r) : O(1) * 二項係数の逆数 1/nCr を返す. * * mint mul(vi rs) : O(|rs|) * 多項係数 nC[rs] を返す.(n = Σrs) * * mint hom(int n, int r) : O(1) * 重複組合せの数 nHr = n+r-1Cr を返す(0H0 = 1 とする) * * mint neg_bin(int n, int r) : O(1) * 負の二項係数 nCr = (-1)^r -n+r-1Cr を返す(n ≦ 0, r ≧ 0) */ class Factorial_mint { int n_max; // 階乗と階乗の逆数の値を保持するテーブル vm fac, fac_inv; public: // n! までの階乗とその逆数を前計算しておく.O(n) Factorial_mint(int n) : n_max(n), fac(n + 1), fac_inv(n + 1) { // verify : https://atcoder.jp/contests/dwacon6th-prelims/tasks/dwacon6th_prelims_b fac[0] = 1; repi(i, 1, n) fac[i] = fac[i - 1] * i; fac_inv[n] = fac[n].inv(); repir(i, n - 1, 0) fac_inv[i] = fac_inv[i + 1] * (i + 1); } Factorial_mint() : n_max(0) {} // ダミー // n! を返す. mint fact(int n) const { // verify : https://atcoder.jp/contests/dwacon6th-prelims/tasks/dwacon6th_prelims_b Assert(0 <= n && n <= n_max); return fac[n]; } // 1/n! を返す(n が負なら 0 を返す) mint fact_inv(int n) const { // verify : https://atcoder.jp/contests/abc289/tasks/abc289_h Assert(n <= n_max); if (n < 0) return 0; return fac_inv[n]; } // 1/n を返す. mint inv(int n) const { // verify : https://atcoder.jp/contests/exawizards2019/tasks/exawizards2019_d Assert(0 < n && n <= n_max); return fac[n - 1] * fac_inv[n]; } // 順列の数 nPr を返す. mint perm(int n, int r) const { // verify : https://atcoder.jp/contests/abc172/tasks/abc172_e Assert(n <= n_max); if (r < 0 || n - r < 0) return 0; return fac[n] * fac_inv[n - r]; } // 二項係数 nCr を返す. mint bin(int n, int r) const { // verify : https://judge.yosupo.jp/problem/binomial_coefficient_prime_mod Assert(n <= n_max); if (r < 0 || n - r < 0) return 0; return fac[n] * fac_inv[r] * fac_inv[n - r]; } // 二項係数の逆数 1/nCr を返す. mint bin_inv(int n, int r) const { // verify : https://www.codechef.com/problems/RANDCOLORING Assert(n <= n_max); Assert(r >= 0 || n - r >= 0); return fac_inv[n] * fac[r] * fac[n - r]; } // 多項係数 nC[rs] を返す. mint mul(const vi& rs) const { // verify : https://yukicoder.me/problems/no/2141 if (*min_element(all(rs)) < 0) return 0; int n = accumulate(all(rs), 0); Assert(n <= n_max); mint res = fac[n]; repe(r, rs) res *= fac_inv[r]; return res; } // 重複組合せの数 nHr = n+r-1Cr を返す(0H0 = 1 とする) mint hom(int n, int r) { // verify : https://mojacoder.app/users/riantkb/problems/toj_ex_2 if (n == 0) return (int)(r == 0); Assert(n + r - 1 <= n_max); if (r < 0 || n - 1 < 0) return 0; return fac[n + r - 1] * fac_inv[r] * fac_inv[n - 1]; } // 負の二項係数 nCr を返す(n ≦ 0, r ≧ 0) mint neg_bin(int n, int r) { // verify : https://atcoder.jp/contests/abc345/tasks/abc345_g if (n == 0) return (int)(r == 0); Assert(-n + r - 1 <= n_max); if (r < 0 || -n - 1 < 0) return 0; return (r & 1 ? -1 : 1) * fac[-n + r - 1] * fac_inv[r] * fac_inv[-n - 1]; } }; //【集合の分割の数(ボールの区別あり,箱の区別なし,箱の中身は 1 個以上,mod 998244353)】O(n log n) /* * 各 j∈[0..n] について,n 点集合をちょうど j 個に分割する方法の数 s(n, k) を格納したリストを返す. * * 利用:【階乗など(法が大きな素数)】 */ vm stirling_S2_allK(int n) { // 参考 : https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E6%95%B0 // verify : https://judge.yosupo.jp/problem/stirling_number_of_the_second_kind //【方法】 // 第 2 種スターリング数の一般項は // s(n, k) // = (1/k!) Σm=[1..k] (-1)^(k-m) bin(k, m) m^n // = Σm=[1..k] ((-1)^(k-m) / (k-m)!) (m^n / m!) // と書け,これは畳込みの形である. vm f(n + 1), g(n + 1); Factorial_mint fm(n); repi(i, 0, n) { f[i] = (i & 1 ? -1 : 1) * fm.fact_inv(i); g[i] = mint(i).pow(n) * fm.fact_inv(i); } vm s = convolution(f, g); s.resize(n + 1); return s; } // int main() { // // input_from_file("input.txt"); // // output_to_file("output.txt"); // int n; // cin >> n; // vm s2 = stirling_S2_allK(n); // repi(i, 0, n) cout << s2[i] << " \n"[i == n]; // } ll n; // mint c(ll x){ // mint ret=0; // for(ll i=x;i>=1;i--){ // ret+=choose(x,i)*mint(i).pow(n)*((x+i)%2==0 ? 1:-1); // } // ret*=choose(n,x); // return ret; // } // mint f(ll x,ll y){ // mint ret=0; // for(ll i=0;i<=y+1;i++){ // ret+=choose(y+1,i)*mint(n-i).pow(x)*mint(-1).pow(i); // } // return ret; // } void solve(){ cin >> n; init_modfact(2000000); vm s2 = stirling_S2_allK(n); // rep(i,s2.size())cout << s2[i].val() << " \n"[i==s2.size()-1]; mint ans=0; for(ll x=1;x<=n;x++){ // printf("c(%lld)/choose(n,%lld) = %d\n",x,x,(c(x)/choose(n,x)/fact[x]).val()); mint fsum=0; // for(ll y=0;y<=x-1;y++){ // fsum+=f(x,y); // ans+=f(x,y)*c(x)*mint(n).pow(n-x); // } fsum=mint(n+1).pow(x)-mint(n).pow(x); ans+=fsum*s2[x]*fact[x]*choose(n,x)*mint(n).pow(n-x); // printf("fcsum(%lld) = %d\n",x,(c(x)*fsum).val()); } cout << ans.val() << endl; } int main(){ // ios::sync_with_stdio(false); // std::cin.tie(nullptr); // ll mx=450; // vc fl(mx+1,0); // for(ll d=2;d<=mx;d++){ // if(fl[d])continue; // ll x=d; // ps.push_back(x); // while(x<=mx){ // fl[x]=1; // x+=d; // } // } ll t = 1; // cin >> t; while (t--){ solve(); } }