頑張った
問題概要
正の整数のみからなる長さ の数列 が与えられる。各 に対して、以下の問に答えよ。
数列 を左から順に「連続する 個の最小値」をとっていってできる長さ の数列が、 の順列になっているかどうかを判定せよ。
制約
- の総和
考えたこと
の場合は自明に判定できるので別途処理しておく。
まず、数列中に 1 が存在しない場合はすべての で No となる。数列中に 1 が存在するときも、
- 1 が左端のみにある
- 1 が右端のみにある
といういずれかの条件を満たさない場合、 を除いて No となる。たとえば などの場合、 の場合以外では 2 回以上 1 が登場してしまうのだ。
1 が左端にあるとしよう (右端の場合も同様)。このとき、その「左端の 1」を除いた長さ の数列について、同様のプロセスで考えることができる。つまり、
- 2 が左端のみにある (そして 1 が存在しない)
- 2 が右端のみにある (そして 1 が存在しない)
といういずれかの条件を満たさない場合、 はすべて No となる。
以下同様に、これを繰り返して区間長が の状態になったとき、
- が左端のみにある (そして 以下の値が存在しない)
- が右端のみにある (そして 以下の値が存在しない)
といういずれかの条件を満たさない場合、 はすべて No となる。
逆に、区間長が の状態で初めて上述の "両端条件" を満たさなくなったとき、 では条件を満たすことも言える。
コード
区間の長さが 1 ずつ減っていく。それぞれの場合について区間の最小値を取る作業を RMQ を使って実施した。この実装の場合、計算量は となる。
#include <bits/stdc++.h> using namespace std; template<class Monoid> struct RMQ { Monoid INF; int SIZE_R; vector<pair<Monoid,int> > dat; RMQ() {} RMQ(int n, const Monoid &inf): INF(inf) { init(n, inf); } void init(int n, const Monoid &inf) { INF = inf; SIZE_R = 1; while (SIZE_R < n) SIZE_R *= 2; dat.assign(SIZE_R * 2, pair<Monoid,int>(INF, -1)); } /* set, a is 0-indexed */ void set(int a, const Monoid &v) { dat[a + SIZE_R] = make_pair(v, a); } void build() { for (int k = SIZE_R - 1; k > 0; --k) { dat[k] = min(dat[k*2], dat[k*2+1]); } } /* update, a is 0-indexed */ void update(int a, const Monoid &v) { int k = a + SIZE_R; dat[k] = make_pair(v, a); while (k >>= 1) dat[k] = min(dat[k*2], dat[k*2+1]); } /* get {min-value, min-index}, a and b are 0-indexed */ pair<Monoid,int> get(int a, int b) { pair<Monoid,int> vleft = make_pair(INF, -1), vright = make_pair(INF, -1); for (int left = a + SIZE_R, right = b + SIZE_R; left < right; left >>= 1, right >>= 1) { if (left & 1) vleft = min(vleft, dat[left++]); if (right & 1) vright = min(dat[--right], vright); } return min(vleft, vright); } inline Monoid operator [] (int a) { return dat[a + SIZE_R].first; } /* debug */ void print() { for (int i = 0; i < SIZE_R; ++i) { Monoid val = (*this)[i]; if (val < INF) cout << val; else cout << "INF"; if (i != SIZE_R-1) cout << ","; } cout << endl; } }; const int INF = 1<<29; int N; vector<int> A; RMQ<int> rmq; void solve(int left, int right, int val, vector<int> &res) { if (left >= right) return; auto mi = rmq.get(left, right); int vmin = mi.first, pmin = mi.second; //cout << "------------" << endl; //COUT(left); COUT(vmin); COUT(val); COUT(right); COUT(mi); if (vmin != val) { for (int i = right - left; i > 1; --i) res[i-1] = 0; return; } if (pmin == left) solve(left + 1, right, val + 1, res); else if (pmin == right - 1) solve(left, right - 1, val + 1, res); else for (int i = right - left - 1; i > 1; --i) res[i-1] = 0; } int main() { int TTT; cin >> TTT; while (TTT--) { cin >> N; A.resize(N); rmq.init(N+10, 1<<29); for (int i = 0; i < N; ++i) { cin >> A[i]; --A[i]; rmq.set(i, A[i]); } rmq.build(); auto B = A; sort(B.begin(), B.end()); bool ok = true; for (int i =0 ; i < N; ++i) if (B[i] != i) ok = false; vector<int> res(N, 1); if (!ok) res[0] = 0; solve(0, N, 0, res); for (int i = 0; i < res.size(); ++i) cout << res[i]; cout << endl; } }