JOI春合宿2020 ビルの飾り付け 4
atcoder.jp
www.ioi-jp.org
問題
2つの数列 A, Bが与えられる。それぞれ長さは2N。
長さ2Nの文字列Sを S_i = 'A' or 'B' とする。
Sに対応して、長さ2Nの数列Cを、S_i = 'A' ならば C_i = A_i、S_i = 'B' ならば C_i = B_i とする。
次の条件をすべて満たすSが存在するならばSを出力せよ。
- 'A'と'B'の出現回数はそれぞれN回
- Cは広義単調増加、つまりC_i <= C_{i+1}
制約
1 <= N <= 500000
解法
S_i が'A'の場合は +1、'B'の場合は -1 として、Sの prefix の和の範囲、つまり最大値と最小値を求める。
- low_a(i) := S_{0 ... i} の和の最小値:ただし C_{0 ... i} が広義単調増加で S_i = 'A' である
- high_a(i) := S_{0 ... i} の和の最大値:ただし C_{0 ... i} が広義単調増加で S_i = 'A' である
- low_b(i) := S_{0 ... i} の和の最小値:ただし C_{0 ... i} が広義単調増加で S_i = 'B' である
- high_b(i) := S_{0 ... i} の和の最大値:ただし C_{0 ... i} が広義単調増加で S_i = 'B' である
この4つの数列は前から順番に計算することができる。条件を満たすことができない場合は low_x(i) = INF, high_x(i) = -INF とする。
'A'と'B'の出現回数は等しい必要があるので、Sの合計が0になるものが解となる。low_a(2N-1) <= 0 <= high_a(2N-1) ならば 末尾が'A'の解が存在する。low_b(2N-1) <= 0 <= high_b(2N-1) ならば、末尾が'B'の解が存在する。
それ以外ならば解は存在しない。
S_i が 'A' で S_{0 … i} の和が w の解が存在するならば、S_{0 … i-1} の和が w-1 の解が存在するはず。
S_i が 'B' で S_{0 … i} の和が w の解が存在するならば、S_{0 … i-1} の和が w+1 の解が存在するはず。
こうして、high, lowを利用して末尾から復元する。
コード
https://atcoder.jp/contests/joisc2020/submissions/15229449
const int MAXN = 1000011; void update(pair<int, int> &dst, const pair<int, int> &src, int add) { amin(dst.first, src.first + add); amax(dst.second, src.second+ add); } int N; int A[MAXN]; int B[MAXN]; const int INF = 1<<29; pair<int, int> PA[MAXN], PB[MAXN]; char ans[MAXN]; void MAIN() { scanf("%d", &N); N += N; REP (i, N) scanf("%d", A+i); REP (i, N) scanf("%d", B+i); REP (i, N) PA[i] = PB[i] = make_pair(INF, -INF); PA[0] = make_pair(1, 1); PB[0] = make_pair(-1, -1); REP (i, N-1) { if (A[i] <= A[i+1]) update(PA[i+1], PA[i], 1); if (B[i] <= A[i+1]) update(PA[i+1], PB[i], 1); if (A[i] <= B[i+1]) update(PB[i+1], PA[i], -1); if (B[i] <= B[i+1]) update(PB[i+1], PB[i], -1); } int idx = N-1; char c = 0; if (PA[N-1].first <= 0 && 0 <= PA[N-1].second) c = 'A'; else if (PB[N-1].first <= 0 && 0 <= PB[N-1].second) c = 'B'; if (c) { int w = 0; while (1) { ans[idx] = c; if (idx == 0) break; int val; if (c == 'A') { w--; val = A[idx]; } else { w++; val = B[idx]; } idx--; if (PA[idx].first <= w && w <= PA[idx].second && A[idx] <= val) c = 'A'; else c = 'B'; } puts(ans); } else { puts("-1"); } }
終わりに
解説スライドが無かったので書いた。