結果
問題 | No.3119 A Little Cheat |
ユーザー |
|
提出日時 | 2025-04-20 03:27:49 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 17,565 bytes |
コンパイル時間 | 4,987 ms |
コンパイル使用メモリ | 269,244 KB |
実行使用メモリ | 413,952 KB |
最終ジャッジ日時 | 2025-04-20 03:29:08 |
合計ジャッジ時間 | 77,395 ms |
ジャッジサーバーID (参考情報) |
judge2 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 25 TLE * 24 |
ソースコード
#ifndef HIDDEN_IN_VS // 折りたたみ用 // 警告の抑制 #define _CRT_SECURE_NO_WARNINGS // ライブラリの読み込み #include <bits/stdc++.h> using namespace std; // 型名の短縮 using ll = long long; using ull = unsigned long long; // -2^63 ~ 2^63 = 9e18(int は -2^31 ~ 2^31 = 2e9) using pii = pair<int, int>; using pll = pair<ll, ll>; using pil = pair<int, ll>; using pli = pair<ll, int>; using vi = vector<int>; using vvi = vector<vi>; using vvvi = vector<vvi>; using vvvvi = vector<vvvi>; using vl = vector<ll>; using vvl = vector<vl>; using vvvl = vector<vvl>; using vvvvl = vector<vvvl>; using vb = vector<bool>; using vvb = vector<vb>; using vvvb = vector<vvb>; using vc = vector<char>; using vvc = vector<vc>; using vvvc = vector<vvc>; using vd = vector<double>; using vvd = vector<vd>; using vvvd = vector<vvd>; template <class T> using priority_queue_rev = priority_queue<T, vector<T>, greater<T>>; 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 rep(i, n) for(int i = 0, i##_len = int(n); i < i##_len; ++i) // 0 から n-1 まで昇順 #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 <class T> inline ll powi(T n, int k) { ll v = 1; rep(i, k) v *= n; return v; } template <class T> inline bool chmax(T& M, const T& x) { if (M < x) { M = x; return true; } return false; } // 最大値を更新(更新されたら true を返す) template <class T> inline bool chmin(T& m, const T& x) { if (m > x) { m = x; return true; } return false; } // 最小値を更新(更新されたら true を返す) template <class T> inline T getb(T set, int i) { return (set >> i) & T(1); } template <class T> inline T smod(T n, T m) { n %= m; if (n < 0) n += m; return n; } // 非負mod // 演算子オーバーロード template <class T, class U> inline istream& operator>>(istream& is, pair<T, U>& p) { is >> p.first >> p.second; return is; } template <class T> inline istream& operator>>(istream& is, vector<T>& v) { repea(x, v) is >> x; return is; } template <class T> inline vector<T>& operator--(vector<T>& v) { repea(x, v) --x; return v; } template <class T> inline vector<T>& operator++(vector<T>& v) { repea(x, v) ++x; return v; } #endif // 折りたたみ用 #if __has_include(<atcoder/all>) #include <atcoder/all> using namespace atcoder; #ifdef _MSC_VER #include "localACL.hpp" #endif using mint = modint998244353; //using mint = static_modint<3>; //using mint = modint; // mint::set_mod(m); 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<mint>; using vvm = vector<vm>; using vvvm = vector<vvm>; using vvvvm = vector<vvvm>; using pim = pair<int, mint>; #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; } 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(v) #define dump_math(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 mint TLE(int n, int m, vi a) { vm dp(m, 1); dump(dp); repi(i, 1, n - 1) { dump("---------- i:", i, "----------"); vm ndp(m); if (a[i - 1] < a[i]) { repi(j, 0, a[i - 1]) { repi(nj, 0, a[i - 1]) ndp[nj] += dp[j]; repi(nj, a[i] + 1, m - 1) ndp[nj] += dp[j]; } repi(j, a[i - 1] + 1, a[i]) { repi(nj, 0, m - 1) ndp[nj] += dp[j]; } repi(j, a[i] + 1, m - 1) { repi(nj, 0, a[i - 1]) ndp[nj] += dp[j]; repi(nj, a[i] + 1, m - 1) ndp[nj] += dp[j]; } } else if (a[i - 1] > a[i]) { repi(j, 0, a[i]) { repi(nj, 0, m - 1) ndp[nj] += dp[j]; } repi(j, a[i] + 1, a[i - 1]) { repi(nj, a[i] + 1, a[i - 1]) ndp[nj] += dp[j]; } repi(j, a[i - 1] + 1, m - 1) { repi(nj, 0, m - 1) ndp[nj] += dp[j]; } } else { repi(j, 0, m - 1) { repi(nj, 0, m - 1) ndp[nj] += dp[j]; } } dp = move(ndp); dump(dp); } dump("- - -"); mint pow_m = mint(m).pow(n - 1); mint res = 0; rep(i, n) res += (m - 1 - a[i]) * pow_m; dump(res); res += m * pow_m; dump(res); rep(j, m) res -= dp[j]; return res; } //【動的遅延評価セグメント木(M-モノイド)】 /* * Dynamic_lazy_segtree<S, op, e, F, act, comp, id>(ll n) : O(1) * v[0..n) = e() で初期化する. * 要素は左作用付きモノイド (S, op, e, F, act, comp, id) の元とする. * * Dynamic_lazy_segtree<S, op, e, F, act, comp, id>(ll n, S x) : O(1) * v[0..n) = x で初期化する. * * set(ll i, S x) : O(log n) * v[i] = x とする. * * S get(ll i) : O(log n) * v[i] を返す(なければ e() を返す) * * S prod(ll l, ll r) : O(log n) * Πv[l..r) を返す.空なら e() を返す. * * apply(ll i, F f) : O(log n) * v[i] = f( v[i] ) とする. * * apply(ll l, ll r, F f) : O(log n) * v[l..r) = f( v[l..r) ) とする. * * ll max_right(ll l, function<bool(S)> f) : O(log n) * f( Πv[l..r) ) = true となる最大の r を返す. * 制約:f( e() ) = true,f は単調 * * ll min_left(ll r, function<bool(S)> f) : O(log n) * f( Πv[l..r) ) = true となる最小の l を返す. * 制約:f( e() ) = true,f は単調 */ template <class S, S(*op)(S, S), S(*e)(), class F, S(*act)(F, S), F(*comp)(F, F), F(*id)()> class Dynamic_lazy_segtree { struct Node { S val; // ノードの値 F lazy; // 遅延させている作用 Node* lc, * rc; // 左右の子 Node() : val(e()), lazy(id()), lc(nullptr), rc(nullptr) {} }; ll n; ll actual_n; // 実際の要素数 Node* root; vector<S> mul; // 子をもつノード t が不変条件を満たすよう子ノードの val から再計算を行う. // 呼び出す際には,子の lazy がいずれも id() でなくてはならない. void update(Node* t) { t->val = op(t->lc->val, t->rc->val); } // ノード t の不変条件を満たしたまま lazy を id() に書き換える. // 呼び出す際には,部分木 t 内の全てのノードで不変条件が満たされなければならない. void eval(Node*& t, ll tL, ll tR) { // ノードが存在しなかった場合は新たに作成する. if (!t) { t = new Node(); t->val = mul[msb(tR - tL)]; } // 遅延させていた作用がなければ何もしない. if (t->lazy == id()) return; // 葉ならすぐに作用させる. if (tR - tL == 1) { t->val = act(t->lazy, t->val); t->lazy = id(); return; } // 遅延作用を子に移す. int b = msb(tR - tL) - 1; if (!t->lc) { t->lc = new Node(); t->lc->val = mul[b]; } t->lc->lazy = comp(t->lazy, t->lc->lazy); if (!t->rc) { t->rc = new Node(); t->rc->val = mul[b]; } t->rc->lazy = comp(t->lazy, t->rc->lazy); // 自身の値に遅延させていた作用を適用する. t->val = act(t->lazy, t->val); t->lazy = id(); } // 部分木 t の位置 pos を値 val にする(部分木 t は区間 [tL..tR) に対応する) void set(Node*& t, ll tL, ll tR, ll pos, S val) { eval(t, tL, tR); // 葉まで降りてきたら値を代入して帰る. if (tR - tL == 1) { t->val = val; return; } // 区間の中央 ll tM = (tL + tR) / 2LL; // 左右いずれかの子に対する処理を行う. if (pos < tM) { set(t->lc, tL, tM, pos, val); eval(t->rc, tM, tR); } else { eval(t->lc, tL, tM); set(t->rc, tM, tR, pos, val); } update(t); } // 部分木 t 内の区間 [tL..tR)∩[l..r) に属する要素の積を返す. S prod(Node*& t, ll tL, ll tR, ll l, ll r) { // [tL..tR) ∩ [l..r) = {} の場合は単位元を返す. if (r <= tL || tR <= l) return e(); eval(t, tL, tR); // [tL..tR) ⊂ [l..r) の場合は区間の総積を返す. if (l <= tL && tR <= r) return t->val; // 区間の中央 ll tM = (tL + tR) / 2LL; // 左右の子からの寄与を求める. S vL = prod(t->lc, tL, tM, l, r); S vR = prod(t->rc, tM, tR, l, r); // それらの積を返す. return op(vL, vR); } // 部分木 t 内の区間 [tL..tR)∩[l..r) に f を作用させる. void apply(Node*& t, ll tL, ll tR, ll l, ll r, F f) { eval(t, tL, tR); // [tL..tR) ∩ [l..r) = {} の場合は何もしない. if (r <= tL || tR <= l) return; // [tL..tR) ⊂ [l..r) の場合は自身の値を更新する. if (l <= tL && tR <= r) { t->lazy = comp(f, t->lazy); //eval(t, tL, tR); return; } // 区間の中央 ll tM = (tL + tR) / 2LL; // 左右の子に f を作用させる. apply(t->lc, tL, tM, l, r, f); apply(t->rc, tM, tR, l, r, f); eval(t->lc, tL, tM); eval(t->rc, tM, tR); update(t); } // 部分木 t に対応する区間 [tL..tR) 内で,f( Πv[l..r) ) = true となる最大の r を返す(acc = Πv[l..tL)) ll max_right(Node* t, ll tL, ll tR, ll l, const function<bool(S)>& f, S& acc) { // [tL..tR) ∩ [l..n) = {} の場合は部分木 t 内には境界はない. if (tR <= l) return actual_n; // 自身の遅延作用を適用する. eval(t, tL, tR); // f( Πv[l..tR) ) = true の場合は部分木 t 内には境界はない. if (l <= tL && f(op(acc, t->val))) { acc = op(acc, t->val); return actual_n; } // 注目している区間の幅が 1 ならば,その区間を含まないギリギリが境界とわかる. if (tR - tL == 1) return tL; // 区間の中央 ll tM = (tL + tR) / 2; // まず左の部分木を見にいき境界の位置を探す. ll res = max_right(t->lc, tL, tM, l, f, acc); // 境界が見つかったならそれを返す. if (res != actual_n) return res; // さもなくば右の部分木を見にいき境界の位置を探す. return max_right(t->rc, tM, tR, l, f, acc); } // 部分木 t に対応する区間 [tL..tR) 内で,f( Πv[l..r) ) = true となる最小の l を返す(acc = Πv[tR..r)) ll min_left(Node* t, ll tL, ll tR, ll r, const function<bool(S)>& f, S& acc) { // [tL..tR) ∩ [l..n) = {} の場合は部分木 t 内には境界はない. if (r <= tL) return 0LL; // 自身の遅延作用を適用する. eval(t, tL, tR); // f( Πv[tL..r) ) = true の場合は部分木 t 内には境界はない. if (tR <= r && f(op(t->val, acc))) { acc = op(t->val, acc); return 0LL; } // 注目している区間の幅が 1 ならば,その区間を含まないギリギリが境界とわかる. if (tR - tL == 1) return tR; // 区間の中央 ll tM = (tL + tR) / 2; // まず右の部分木を見にいき境界の位置を探す. ll res = min_left(t->rc, tM, tR, r, f, acc); // 境界が見つかったならそれを返す. if (res != 0LL) return res; // さもなくば左の部分木を見にいき境界の位置を探す. return min_left(t->lc, tL, tM, r, f, acc); } public: // v[0..n) = e() で初期化する. Dynamic_lazy_segtree(ll n_) : actual_n(n_), root(nullptr) { // verify : https://atcoder.jp/contests/pakencamp-2024-day1/tasks/pakencamp_2024_day1_p int B = n_ == 0 ? 0 : msb(n_ - 1) + 1; n = 1LL << B; mul = vector<S>(B + 1, e()); } // v[0..n) = x で初期化する. Dynamic_lazy_segtree(ll n_, S x) : actual_n(n_), root(nullptr) { // verify : https://judge.yosupo.jp/problem/range_affine_range_sum_large_array int B = n_ == 0 ? 0 : msb(n_ - 1) + 1; n = 1LL << B; mul = vector<S>(B + 1); mul[0] = x; repi(b, 1, B) mul[b] = op(mul[b - 1], mul[b - 1]); } Dynamic_lazy_segtree() : n(0LL), root(nullptr) {} // v[i] = x とする. void set(ll i, S x) { Assert(0LL <= i); Assert(i < actual_n); set(root, 0LL, n, i, x); } // v[i] を返す. S get(ll i) { Assert(0LL <= i); Assert(i < actual_n); return prod(root, 0LL, n, i, i + 1); } // Πv[l..r) を返す.空なら e() を返す. S prod(ll l, ll r) { // verify : https://judge.yosupo.jp/problem/range_affine_range_sum_large_array chmax(l, 0LL); chmin(r, actual_n); if (l >= r) return e(); return prod(root, 0LL, n, l, r); } // v[i] = f( v[i] ) とする. void apply(ll i, F f) { apply(root, 0LL, n, i, i + 1, f); } // v[l..r) = f( v[l..r) ) とする. void apply(ll l, ll r, F f) { // verify : https://judge.yosupo.jp/problem/range_affine_range_sum_large_array apply(root, 0LL, n, l, r, f); } // f( Πv[l..r) ) = true となる最大の r を返す. ll max_right(ll l, const function<bool(S)>& f) { chmax(l, 0LL); S acc = e(); Assert(f(e())); return min(max_right(root, 0LL, n, l, f, acc), actual_n); } // f( Πv[l..r) ) = true となる最小の l を返す. ll min_left(ll r, const function<bool(S)>& f) { chmin(r, actual_n); S acc = e(); Assert(f(e())); return min_left(root, 0LL, n, r, f, acc); } #ifdef _MSC_VER friend ostream& operator<<(ostream& os, Dynamic_lazy_segtree& seg) { rep(i, seg.actual_n) os << seg.get(i) << " "; return os; } #endif }; //【変更 作用付き 総和 可換モノイド】 /* * S ∋ x = {v, c} : c 個の元の和で値 v をとっていることを表す. * F ∋ f = b : 零次関数 f(x) = 0 x + b を表す. * x op y : cx + cy 個の元の和で値 vx + vy をとっている状態にする. * f act x : c 個の元の和で値 c f をとっている状態にする. * f comp g : 合成した零次関数 f o g を返す. */ // verify : https://atcoder.jp/contests/abc237/tasks/abc237_g using T109 = mint; using S109 = pair<T109, T109>; // ベクトル (v, c) using F109 = T109; // 行列 (0, f; 0, 1) S109 op109(S109 x, S109 y) { auto [vx, cx] = x; // ベクトル (vx, cx) auto [vy, cy] = y; // ベクトル (vy, cy) // (vx, cx) + (vy, cy) = (vx + vy, cx + cy) return { vx + vy, cx + cy }; } S109 e109() { return { T109(0), T109(0) }; } F109 id109() { return T109(INFL + 1); } // 使わない値なら何でも OK S109 act109(F109 f, S109 x) { if (f == id109()) return x; auto [v, c] = x; // ベクトル (v, c) // (0, f; 0, 1).(v, c) = (f c, c) return { f * c, c }; } F109 comp109(F109 f, F109 g) { if (f == id109()) return g; // (0, f; 0, 1).(0, g; 0, 1) = (0, f; 0, 1) return f; } #define Update_Sum_mmonoid S109, op109, e109, F109, act109, comp109, id109 mint solve(int n, int m, vi a) { Dynamic_lazy_segtree<Update_Sum_mmonoid> seg(m, S109{1, 1}); dump(seg); repi(i, 1, n - 1) { dump("---------- i:", i, "----------"); if (a[i - 1] < a[i]) { auto sL = seg.prod(0, a[i - 1] + 1).first; auto sM = seg.prod(a[i - 1] + 1, a[i] + 1).first; auto sR = seg.prod(a[i] + 1, m).first; seg.apply(0, a[i - 1] + 1, sL + sM + sR); seg.apply(a[i - 1] + 1, a[i] + 1, sM); seg.apply(a[i] + 1, m, sL + sM + sR); } else if (a[i - 1] > a[i]) { auto sL = seg.prod(0, a[i] + 1).first; auto sM = seg.prod(a[i] + 1, a[i - 1] + 1).first; auto sR = seg.prod(a[i - 1] + 1, m).first; seg.apply(0, a[i] + 1, sL + sR); seg.apply(a[i] + 1, a[i - 1] + 1, sL + sM + sR); seg.apply(a[i - 1] + 1, m, sL + sR); } else { auto sM = seg.prod(0, m).first; seg.apply(0, m, sM); } dump(seg); } dump("- - -"); mint pow_m = mint(m).pow(n - 1); mint res = 0; rep(i, n) res += (m - 1 - a[i]) * pow_m; dump(res); res += m * pow_m; dump(res); res -= seg.prod(0, m).first; return res; } int main() { // input_from_file("input.txt"); // output_to_file("output.txt"); int n, m; cin >> n >> m; vi a(n); cin >> a; --a; dump(TLE(n, m, a)); dump("================"); EXIT(solve(n, m, a)); }