#pragma GCC optimize("O3") #include using namespace std; #define rep(i,a,b) for (int i=(int)(a);i<(int)(b);i++) #define all(p) p.begin(),p.end() #include using mint = atcoder::modint998244353; // ローリングハッシュの base int base = 10; #include struct F{ mint v = 0; mint b = 1; }; F op(F l, F r){ l.v *= r.b; l.v += r.v; l.b *= r.b; return l; } F e(){ return F{}; } // 今回の base = 10 は、 // 998244353 の原始根 bool f(F x){ return x.b == 1; } int op_max(int a, int b){ return max(a, b); } int e_max(){ return -1; } int target; bool g(int x){ return x < target; } int op_min(int a, int b){ return min(a, b); } int e_min(){ return (1 << 30); } bool h(int x){ return target <= x; } vector solve(vector p){ int N = p.size(); // p 自体の hash atcoder::segtree p_hash(N); rep(i, 0, N) p_hash.set(i, {p[i], base}), p[i]--; vector inv(N); rep(i, 0, N) inv[p[i]] = i; // p の max の列 atcoder::segtree p_max(p); // p の min の列 atcoder::segtree p_min(p); // 区間であって、先頭より小さいものの最小値 atcoder::segtree cut_min(N); // スペシャル mex というのを定義する。 // 区間 [l, r) のスペシャル mex というのは、 // p[l] 以上かつ、先頭である最小の数を m としたとき // l < i < r かつ m < p[i] を満たす最小の index i // これが存在しないとき、-1 とする atcoder::segtree mex(N); // 答えの hash atcoder::segtree ans_seg(N); // 分割した順列を、その並び順で並べる atcoder::segtree perm_seg(N); // スペシャル mex を計算する。 auto sp_mex = [&](int ind) -> void { int m = ans_seg.max_right(p[ind] + 1); int r = perm_seg.max_right(ind + 1); target = m; int nr = p_max.max_right(ind); if (nr < r) mex.set(p[ind], nr); else mex.set(p[ind], -1); }; // ある区間の cut_min を計算する auto calc_cut_min = [&](int ind) -> int { int r = perm_seg.max_right(ind + 1); auto res = p_min.prod(ind + 1, r); if (p[ind] < res) return -1; else return res; }; auto set_calc_min = [&](int ind) -> void { auto tmp = calc_cut_min(ind); if (tmp != -1){ cut_min.set(tmp, tmp); } }; auto seg_set = [&](int l, int r) -> void { // cerr << "seg set " << l << " " << r << endl; perm_seg.set(l, p_hash.prod(l, r)); ans_seg.set(p[l], perm_seg.get(l)); }; seg_set(0, N); // 答えを変更する。 // cut(i) で、i の直前で切る auto cut = [&](int i) -> bool { if (perm_seg.get(i).b == 1){ int a = perm_seg.min_left(i) - 1; int c = perm_seg.max_right(i + 1); // cut min を除く { int tmp = calc_cut_min(a); if (tmp != -1){ cut_min.set(tmp, e_min()); } } seg_set(a, i); seg_set(i, c); // スペシャル mex を計算させる // 計算対象は、a, i はもちろん // p[i] の一個前も必須 sp_mex(a); sp_mex(i); { int tmp = ans_seg.min_left(p[i]); if (tmp) sp_mex(inv[tmp - 1]); } // cut min を求める set_calc_min(a); set_calc_min(i); return true; } return false; }; // 実際の返り値 vector res(N); // res の何番目まで埋めたか int ind = 0; res[0] = ans_seg.all_prod().v; ind = 1; // n <- 先頭に持っていきたい値 rep(n, 0, N){ if (n == 0){ if (p[0]){ cut(inv[0]); res[ind] = ans_seg.all_prod().v; ind++; } continue; } // n - 1 まで並んでいる。 // n を次の番にできるか? // n - 1, n の順で並んでいたら、何もしなくていい if (p[0] != n && inv[n - 1] + 1 == inv[n]) continue; //やるべきことは? // n - 1 の後ろを切る int back_cut = 0; if (p[N - 1] != n - 1 && perm_seg.get(inv[n - 1] + 1).b == 1){ back_cut = 1; } // n の前を切る int front_cut = 0; if (perm_seg.get(inv[n]).b == 1) front_cut = 1; if (back_cut + front_cut == 0) continue; // 適当に切る必要がある if (back_cut + front_cut == 2){ // 切るべき場所を探す int tmp = ans_seg.min_left(n) - 1; // ここの部分は嘘のはず int T = 10000; while (true){ if (tmp > cut_min.all_prod() || tmp == N) break; T--; if (T == 0){ tmp = N; break; } int nx = ans_seg.max_right(tmp + 1); int id = inv[tmp] + 1; bool ok = false; while (id != N && perm_seg.get(id).b == 1 && T > 0){ if (nx < p[id]){ ok = true; break; } id++; T--; } if (T == 0){ tmp = N; break; } if (ok) break; tmp = nx; } // 先頭より小さいの消し if (tmp > cut_min.all_prod()){ tmp = cut_min.all_prod(); int m = inv[tmp]; int l = perm_seg.min_left(m) - 1; int r = perm_seg.max_right(m); // [l, r) -> [l, m), [m, r) seg_set(l, m); seg_set(m, r); res[ind++] = ans_seg.all_prod().v; seg_set(l, r); seg_set(m, m); } // 切る場所がなかったらそのまま else if (tmp == N){ res[ind] = res[ind - 1]; ind++; } else{ int l = inv[tmp]; int r = perm_seg.max_right(l + 1); int m = mex.get(tmp); // [l, r) -> [l, m), [m, r) seg_set(l, m); seg_set(m, r); res[ind++] = ans_seg.all_prod().v; seg_set(l, r); seg_set(m, m); } } if (back_cut) cut(inv[n - 1] + 1); if (front_cut) cut(inv[n]); res[ind] = ans_seg.all_prod().v; ind++; } while (ind != N) res[ind] = res[ind - 1], ind++; return res; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--){ int N; cin >> N; vector p(N); rep(i, 0, N){ cin >> p[i]; } auto ans = solve(p); rep(i, 0, N){ cout << ans[i].val() << (i + 1 == N ? "\n" : " "); } } }