// Author: lzm0107 #include using namespace std; constexpr int kMod = 998'244'353, kInf32 = 0x3f3f3f3f; constexpr int kMaxN = 200'000; int pw10[kMaxN * 2 + 10]; struct Info0 { int min_val, max_val, hash_val, len; Info0() : min_val(kInf32), max_val(~kInf32), hash_val(0), len(0) {} Info0(int _min_val, int _max_val, int _hash_val, int _len) : min_val(_min_val), max_val(_max_val), hash_val(_hash_val), len(_len) {} Info0 operator+(Info0 rhs) const { return { min(min_val, rhs.min_val), max(max_val, rhs.max_val), (int)(((long long)hash_val * pw10[rhs.len] + rhs.hash_val) % kMod), len + rhs.len}; } Info0 &operator+=(Info0 rhs) { return *this = *this + rhs; } }; struct Info1 { Info0 d; int min_head, max_head; bool chance; Info1() : d(), min_head(kInf32), max_head(~kInf32), chance(false) {} Info1(Info0 _d, int x) : d(_d), min_head(x), max_head(x), chance(_d.min_val < x) {} Info1(Info0 _d, int _min_head, int _max_head, bool _chance) : d(_d), min_head(_min_head), max_head(_max_head), chance(_chance) {} Info1 operator+(Info1 rhs) const { return { d + rhs.d, min(min_head, rhs.min_head), max(max_head, rhs.max_head), chance || rhs.chance || max_head > rhs.d.min_val || d.max_val > rhs.min_head}; } Info1 &operator+=(Info1 rhs) { return *this = *this + rhs; } }; struct SegmentTree0 { Info0 d[(4 << __lg(kMaxN + 1)) + 10]; int pn; void Build(int *arr, int n) { pn = 2 << (31 ^ __builtin_clz(n + 1)); for (int i = 1; i < pn * 2; i++) { d[i] = Info0(); } for (int i = 1; i <= n; i++) { d[i + pn] = {arr[i], arr[i], arr[i], 1}; } for (int i = pn - 1; i >= 1; i--) { d[i] = d[i << 1] + d[i << 1 | 1]; } } Info0 Query(int l, int r) { Info0 res_l, res_r; for (l += pn - 1, r += pn + 1; l ^ r ^ 1; l >>= 1, r >>= 1) { if (l & 1 ^ 1) { res_l += d[l ^ 1]; } if (r & 1) { res_r = d[r ^ 1] + res_r; } } return res_l + res_r; } int FindFirstGE(int pos, int val) { for (pos += pn; pos > 0 && ((pos & 1) || d[pos ^ 1].max_val < val); pos >>= 1) {} if (pos == 0) { return -1; } for (pos ^= 1; pos < pn; pos = pos << 1 | (d[pos << 1].max_val < val)) {} return pos - pn; } } sgt0; struct SegmentTree1 { Info1 d[(4 << __lg(kMaxN + 1)) + 10]; int pn; void Build(int n) { pn = 2 << (31 ^ __builtin_clz(n + 1)); for (int i = 1; i < pn * 2; i++) { d[i] = Info1(); } } void Set(int pos, Info1 val) { pos += pn; d[pos] = val; for (pos >>= 1; pos > 0; d[pos] = d[pos << 1] + d[pos << 1 | 1], pos >>= 1) {} } array FindBetterSuffix() { Info1 sum; int pos = 1; for (; pos < pn;) { if (d[pos << 1].chance || d[pos << 1].max_head > min(d[pos << 1 | 1].d.min_val, sum.d.min_val) || d[pos << 1].d.max_val > min(d[pos << 1 | 1].min_head, sum.min_head)) { sum = d[pos << 1 | 1] + sum; pos <<= 1; } else { pos = pos << 1 | 1; } } return {pos - pn, min(d[pos].d.min_val, sum.d.min_val), sum.min_head}; } } sgt1; int n, p[kMaxN + 10]; set segment_start; void Flip(int x) { int l, r; if (segment_start.count(x)) { auto it = segment_start.find(x); l = *prev(it), r = *next(it); segment_start.erase(it); sgt1.Set(p[l], Info1(sgt0.Query(l, r - 1), p[l])); sgt1.Set(p[x], Info1()); } else { auto it = segment_start.insert(x).first; l = *prev(it), r = *next(it); sgt1.Set(p[l], Info1(sgt0.Query(l, x - 1), p[l])); sgt1.Set(p[x], Info1(sgt0.Query(x, r - 1), p[x])); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); pw10[0] = 1; for (int i = 1; i <= kMaxN; i++) { pw10[i] = 10ll * pw10[i - 1] % kMod; } int T; cin >> T; while (T--) { static int ans[kMaxN + 10], q[kMaxN + 10]; cin >> n; for (int i = 1; i <= n; i++) { cin >> p[i]; q[p[i]] = i; } sgt0.Build(p, n); sgt1.Build(n); sgt1.Set(p[1], Info1(sgt0.Query(1, n), p[1])); segment_start = {1, n + 1}; for (int cur = 0; cur <= n; cur++) { ans[segment_start.size() - 1] = sgt1.d[1].d.hash_val; if (cur == n) { break; } bool cond0 = cur > 0 && q[cur] != n && p[q[cur] + 1] > cur + 1; bool cond1 = q[cur + 1] > 1 && p[q[cur + 1] - 1] > cur + 1; if (cond0 && cond1) { auto [x, y, z] = sgt1.FindBetterSuffix(); int pos; if (y < x) { pos = q[y]; } else { pos = sgt0.FindFirstGE(q[x], z); } if (pos == -1) { ans[segment_start.size()] = ans[segment_start.size() - 1]; } else { Flip(pos); ans[segment_start.size() - 1] = sgt1.d[1].d.hash_val; Flip(pos); } } if (cond0) { Flip(q[cur] + 1); } if (cond1) { Flip(q[cur + 1]); } } for (int i = segment_start.size(); i <= n; i++) { ans[i] = ans[i - 1]; } for (int i = 1; i <= n; i++) { cout << ans[i] << ' '; } cout << '\n'; } return 0; }