−目次
AtCoder Beginner Contest 403(Promotion of AtCoder Career Design DAY)F,G問題メモ
F - Shortest One Formula
問題文
- 正整数 N が与えられます。
- 1, +, *, (, ) のみからなる数式のうち、値が N となるものの中で、文字列としての長さが最小のものを一つ求めてください。
制約
- 1≤N≤2000
- 入力は全て整数
解法
N の数式表現は、「直接“1”だけで作れる」もので無い限りは、何らかの値 k を使って、以下のいずれかの形になる。
- [k の数式表現]+[N−k の数式表現]
- [k の数式表現]*[N/k の数式表現]
よって、1,2,...,N について順に「最も短い数式表現」をDPで求めていける。
ただしかけ算を使う場合は、左項や右項に「括弧でくくられていない足し算」があったら括弧でくくらないといけない。
でも、最終的な答えは別に括弧でくくる必要は無い。
そのままかけ算できる形なのかどうか、分けて情報を持たせる必要がある。
- DP1[i]:= 値が i となる数式で「直接かけ算できる」もののうち、長さが最小のものの長さ。
- DP2[i]:= 値が i となる数式で長さが最小のものの長さ。直接かけ算できるとは限らない。
(例) 1+11∗11 のように括弧でくくられていない足し算がある形は DP1 の対象には含まれない。(1+11)∗11 や (1+11∗11) なら含まれる。
また、復元のために以下も記録する。
- TB1[i]:=DP1[i] を達成する方法の一例
- TB2[i]:=DP2[i] を達成する方法の一例
- 持たせる情報例
- (0,): '111..' という形なので直接作れる。
- (1,x,y): 値が x になる式と y になる式を足し算で結合し括弧で囲んだ。
- (2,x,y): 値が x になる式と y になる式を足し算で結合した。括弧では囲んでない。
- (3,x,y): 値が x になる式と y になる式をかけ算で結合した。
O(N2) が間に合うので、各 i につき「2つの足し算に分解する方法」「2つのかけ算に分解する方法」を全て試せばよい。
途中経過を文字列として持つと時間が危ないかな? と思い復元情報のみ持たせるようにしたが、 値が n になる最短文字列長は O(logn) であると示せるので、直接持たせても O(N2logN) で大丈夫みたい。
G - Odd Position Sum Query
問題文
- 空の数列 A があります。
- Q 個のクエリが与えられるので順に処理してください。i 番目のクエリは以下で説明されます。
- 整数 yi が与えられる。
- z を、i=1 のときは 0、それ以外のときは i−1 番目のクエリに対する答えとする。
- xi を xi=((yi+z)mod109)+1 で定義する。
- A の末尾に xi を追加する。
- その後、A を昇順に並べた列を B=(B1,B2,…,Bi) としたとき B の奇数番目の要素の総和を求めよ。
- すなわち、i 以下の最大の奇数を m としたとき B1+B3+B5+…+Bm を求めよ。
制約
- 1≤Q≤3×105
- 0≤yi<109
- 1≤xi≤109
- 入力は全て整数
解法
入力として与えられるのは yi だが、クエリの実質的な入力は yi から計算で求める xi である。ただし計算に前のクエリの答えを必要とする。
これは「クエリを先読みして解く」ことを禁じる出題方法の1つと考えられる。要は「与えられた順で解けよ」と言っている。
わざわざ禁止しているということは「クエリが先読みできたら楽に解ける」可能性が高い。
先読みできない場合と全く別の解法となる可能性もあるが、ひとまず「クエリを先読みできたとしたら?」を考えてみる。
すると、前もって座標圧縮しておけば、1点更新、区間集計のセグメント木で解けることが分かる。
各ノードは Ai の値の範囲を示し、[l,r) を表すノードには以下を持たせると良い。
- 現時点の l 以上 r 未満の Ai の個数
- 現時点の l 以上 r 未満の Ai の小さい方から奇数番目の要素の和
- 現時点の l 以上 r 未満の Ai の小さい方から偶数番目の要素の和
左の区間と右の区間をマージするとき、左の区間の個数の偶奇によって、 右の区間の奇数番目・偶数番目を反転させて合算するのか、そのまま合算するのかを切り替えればよい。
ただ、制約が1≤xi≤109 であるため、値を基準にセグメント木を作るには 109 オーダーの空間計算量が必要となる。
先読みできれば座標圧縮で対応できるが、今回はできない。
そこで、動的セグメント木を使う。「必要なところだけ作るセグメント木」とも呼ばれている。
まぁ、こういうデータ構造を知っているかどうか、という側面が大きい。
Pythonでは実装をちゃんとしないとTLEしてしまった。
(自作ノードクラスを作成する、再帰関数を使う、などの実装が重くなりがち。あと入力によっては辞書を使う場合)
普通に通っている人もいるので、ちょっとした実装の工夫次第で速くなるとは思うのだが。。。