目次
AtCoder Regular Contest 173 A,B,C,D問題メモ
ケアレスな実装ミスでタイムロスしちまうのはなおらないねえ。
A - Neq Number
問題
- ある正整数 $X$ を(先頭に0のつかない)十進法表記した際に、同じ数字が隣り合わない場合、$X$ を“Neq Number” という
- 小さい方から $K$ 番目の Neq Number を求めよ
- 1つの入力につき $T$ ケース与えられる
- $1 \le T \le 100$
- $1 \le K \le 10^{12}$
解法
「$d$ 桁の Neq Number は何個あるか?」は、簡単に求められる。
- 一番大きい桁は $1~9$ の9通り
- 続く $d-1$ 個の桁は $0~9$ のうち前の桁と違う数字の9通り
- → 全部で $9^d$ 個ある
なので、まず「$d$ 桁以下のNeq Numberの個数」を事前計算しておくと、$K$ 番目のNeqNumberが何桁かは判明する。
同様に、大きい方から何個かの先頭桁を決めたときに「これに当てはまる Neq Number は何個あるか?」も求められる。
各桁、前の桁の数と違う数の選択肢があるので、$9^{未定桁数}$ ある。
1 2 3 4 ? ? ? ? それぞれの"?"に適当な数を置いてできるNeqNumberは、9^4=6561個ある
$i$ 桁目に置く数を順に試す。答えが 123?????
を満たす中で 20000 番目である場合、
i 1 2 3 0 ? ? ? ? 1~ 6561 1 2 3 1 ? ? ? ? 6562~13122 1 2 3 2 ? ? ? ? 13123~19683 1 2 3 3 ? ? ? ? ←×違反 1 2 3 4 ? ? ? ? 19684~26244 → 答えは、1234???? を満たす中で 20000 - 19683 = 317番目
なので、大きい方の桁から $0,1,2,...,9$ のうち前の桁と異なるものを置いていき、個数を数えていくと、順に決めていける。
1つにつき、進数を $B=10$、$K_{max}$ 番目のNeqNumberの桁数を $D$ として($D=14~15$ くらい)、$O(BD)$ で求められる。
B - Make Many Triangles
問題
- 2次元空間上に $N$ 個の点がある
- これらの点を頂点に持つ(非退化な)三角形をできるだけ多く作りたい
- 1度使った点は他の三角形では使えないとき、最大何個作れるか、求めよ
- $3 \le N \le 300$
解法
基本的には、$\left \lfloor \dfrac{N}{3} \right \rfloor$ 個作れるはず。
しかし同一直線上にある3点は、三角形が作れないので選ぶことができない。(作れないのはこの条件のみ)
点同士を結ぶ直線のうち、最も多くの点が並んでいる直線を $L$、そこに乗っている点の数を $C$ とする。
$L$ 上の点をなるべく多く消費することを考えると、$L$ 上から2点と、それ以外から1点を選ぶことで三角形が作れる。
よって、そうやって作っていっても $L$ 上に点が残る場合、作れる個数は $N-C$ 個となる。
答えは、$\min(\left \lfloor \dfrac{N}{3} \right \rfloor,N-C)$ となる。
同一直線上に並ぶ点のカウント
「同一直線上に並ぶ点の個数」を求めるには、2点組ごとに「2点を結ぶ直線の方程式 $ax+by+c=0$ の係数 $a,b,c$」を計算し、それごとにカウントする。
$c$ 個の点が並ぶ直線は、$\dfrac{c(c-1)}{2}$ 回カウントされることになるので、
最も多くカウントされた直線の値を逆算すると個数が分かる。
$(x_1,y_1)$ と $(x_2,y_2)$ を通る直線の方程式を、「小数による誤差を排除して」「$x$ 軸や $y$ 軸に平行な場合にも対応して」求めるには、以下の式を使うのがよい。
- $d_x=x_2-x_1,d_y=y_2-y_1$ として、$d_yx - d_xy + (d_xy_1 - d_yx_1)=0$
ただし、係数に定数倍をかけたものは同一になるので、それらを統一する必要がある。
- $d_x$ と $d_y$ は、GCDで割って互いに素にしておく
- $d_x$ が負の場合、正にする
- $d_x$ が0かつ $d_y$ が負の場合、$d_y$ を正にする
こうすると、定数倍の違いを吸収して、3つの整数値で直線を特定できる。
C - Not Median
問題
- $1~N$ の順列 $A=(A_1,...,A_N)$
- 各 $i=1,...,N$ につき、以下を満たす区間 $[l,r]$ があるか判定し、存在する場合は長さ $r-l+1$ の最小値を求めよ
- 区間長は奇数
- $i$ を含む($l \le i \le r$)
- $(A_l,A_{l+1},...,A_r)$ の中央値は、$A_i$ ではない
- $1 \le N \le 3 \times 10^5$
解法
最小の区間は3である。特に、$i$ の左右が、$A_i$ より小さい同士/大きい同士で一致していれば、確実に3となる。
i 両隣が Ai より大きい同士なら、 A 8 5 6 [i-1, i+1] の区間にすれば確実に最小の3になる
それより長くなる場合というのは?
$A_i$ 自身より大きい数と小さい数が左右に交互に現れ、 1つずらしても「小さい数が出て小さい数が入る」「大きい数が出て大きい数が入る」ようにしかならない場合に、長くなる。
i j A 7 14 6 8 10 2 9 5 4 ... ^:8より大きい v ^ v ^ v ^ v v v:8より小さい ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~ i を含む長さ5の区間は、 ~~~~~~~~~~~~~~ 全て8が中央値になる
上例 5 4
のように大きい数同士、小さい数同士が隣り合った箇所($j$)を含めた区間にすると、中央値を変えられる。
(※端の場合を除く。後述)
このように、$A_i$ より大きい同士/小さい同士が隣り合う箇所を単に「連続箇所」とする。
各 $i$ につき、左右で最も近い連続箇所のindexを高速に求めたい。
区間に連続箇所を含められればよいので、もう一方の端は、$i$ をギリギリ含めるか、もしくは1つだけ逆側を含めるかになる。
左右で近い方の連続箇所との距離を、奇数に調整したものが、答えとなる。
i v連続箇所 A 7 14 6 8 10 2 9 5 4 ... v ^ v ^ v ^ v v ~~~~~~~~~~~~~~~~~~~~ 連続箇所から i を挟んで逆側(6)を、1つだけ含めて調整した場合
端の場合
端の場合、「逆側を1つだけ含める」ができない。
左端なら、区間を2個ずつ右に伸ばすしかない。
i A 8 10 2 9 5 4 12 ... ^ v ^ v v ^
その場合、小さい同士が連続(5 4
)していても、次が大きい(12
)なら、やはり中央値は8である。
左端・右端は、愚直に2個ずつ伸ばして確かめていくのがよい。
※端で無い場合は、「$A_i$ の左右の大小は異なる」前提から、この心配は無い。
例えば $i$ を左端、右側の連続箇所を右端とした区間が
- 長さが奇数なら、それでよし
- 長さが偶数なら、もう1要素、左か右に追加しなければならないが、、、
- 「小 i 大 小 大 小 小」のように、偶奇を考えると逆側の1個と、連続する値の大小は必ず一致する
最も近い連続箇所の求め方
複数の求め方がある。
- セグメント木
- SortedSet
- 実は愚直で間に合う
セグメント木
$A_i$ の小さい方から順に答えを求めていく。
現在、答えを求めている $A_i$ を $k$ とする。
$B=(B_1,...,B_{N-1})$ を、「$A_i$ と $A_{i+1}$ が、ともに $k$ より小さい、またはともに $k$ より大きい場合に1、それ以外は0」を取る値とする。
A 7 14 6 8 10 2 9 5 4 12 11 3 13 1 B 1 1 1 1 1 1 1 1 1 1 1 1 1 k=1 のとき B 0 0 0 1 0 0 0 1 0 1 0 0 0 k=8 のとき
$B$ を区間Maxセグメント木に載せて、その上で二分探索することによって、 「$[l,r)$ の範囲のうち最も左にある1」「最も右にある1」の位置を取得できる。
これで、左右に最も近い連続箇所の位置が求められる。
$k=A_4=8$ の答えを求め終わったら、次の $k$ に向けて、$A_3,A_4,A_5$ の大小を調べて $B_3,B_4$ を更新する。
愚直
$i$ から左/右に1つずつ、連続箇所が出てくるまで調べる実装でも、間に合う。
ある $i$ で時間がかかる場合というのは、左右に $A_i$ より大/小が交互に出現する場合である。
このとき、通過した各要素は、その要素にとっては「両端が大きい同士/小さい同士」に該当するので、答えは3とすぐに求まる。
i x をつけた要素は、 A 7 14 6 8 10 2 9 5 4 ... 両端が大きい同士/小さい同士なので答えは3 v ^ v ^ v ^ v v ←←←←←|→→→→→→→→ ←iからの探索範囲 x x x x x
また、愚直に調べる対象となる $i$ は、小→中(i)→大 と並んでいるので、 他の要素から見れば、$i$(の周辺 $\pm1$)で大/小が連続し、そこで探索が終了することになる。
よって、$A$ の各要素が愚直に調べられる回数は、左方向・右方向に1回ずつとなり、$O(N)$ で間に合う。
小 中 大 大 中 小 A 7 14 6 8 10 2 9 5 4 ... v ^ v ^ v ^ v v |→→→→→→→→ ←←←←←←|
D - Bracket Walk
問題
- $N$ 頂点 $M$ 辺の有向グラフ
- 自己辺・多重辺は存在しない
- 全体が強連結(どの頂点からどの頂点へも経路が存在する)
- 各辺には、'(' または ')' のいずれか一方が書かれている
- 以下のようなウォーク(同じ辺・頂点を複数回通ってもよい移動経路)が存在するか判定せよ
- 始点と終点が同じ頂点
- 全ての辺を1度以上通る
- 通った辺に書かれた括弧を順に並べると、正しい括弧列になっている
- $2 \le N \le 4000$
- $N \le M \le 8000$
解法
以下、単に「ウォーク」というと、始点と終点が同じ「閉じたウォーク」を指すとする。
条件の整理
まず、正しい括弧列というと以下の条件をともに満たすものだが、
- '(' と ')' が同数
- 途中で ')' の個数が '(' の個数を上回ってはいけない
このうち、後者は無視できる。
もし途中で ')' の個数が上回ってしまっても、開始位置を適切にずらすことで、必ず正しい括弧列にできる。
よって、「'(' と ')' が同数」で全辺を通るウォークを見つけさえすればよい。
同数とは、「'(' を $+1$、')' を $-1$ で置き換えて、ウォークのコストが0」と言い換えられる。
条件を満たすウォークがあったとして、存在を見つける上では頂点 $1$ を始点終点に固定して考えて問題ない。
$1$ を始点と終点とするウォークを $Walk_1$ とする。
$Walk_1$ を複数個並べたものもまた、$Walk_1$ である。
答えとなる「全辺を通りコストが0の $Walk_1$」を、
$Walk_1$ の結合で作ることを考える。
コストの正負による分類
ウォークのコストは、当然ながら 正、0、負 のいずれかである。
グラフ中にコスト正の閉路があることと、コスト正の $Walk_1$ があることは、一致する。
- ⇒の証明
- 閉路が $1$ を含んでいればそれ自体がウォークである
- $1$ を含まなくても、強連結なので、$1$ から閉路まで行き、好きなだけ回って帰る、ということができる
- ⇐の証明
- 閉じたウォークは、複数の閉路に分解できる(同じ頂点に2回以上訪れていたら、そこで分割することを繰り返せばよい)
- コストが0や負の閉路だけでは、明らかにコスト正のウォークは構成できない
コスト正・負のウォークがそれぞれあるとどうなるか考える。
正と負が両方存在する場合
条件を満たすウォークを実現できる。
- 未使用辺を1つ適当に取る
- それを含む $Walk_1$ を適当に1つ決める
- $Walk_1$ のコストが正なら、別のコスト負の $Walk_1$、負なら正の $Walk_1$ を適当に1つ決める
- 2つを何回かぐるぐるすることで、総コストを0にあわせられる
- 2つの $Walk_1$ で使用した辺を使用済み辺として、最初に戻る
- 全ての辺を使用したら完了
全ての閉路のコストが0の場合
意図的に作らない限り珍しいが、これもあり得る。
この場合も、当然コスト0を実現できる。
正か負のどちらか一方のみが存在する場合
なんとなく「No」っぽいが、証明しようとすると悩ましい。公式Editorialでは背理法による証明がある。
正の閉路があり負の閉路が無いグラフでも、条件を満たすウォークがあったと仮定する。
あるウォーク $W$ と、そこに含まれる辺のみを使用した閉路 $C$ があるとする。
この時、以下のようにすることを、“$W$ から $C$ を取り除く”と表現するとする。
- $W$ で使われた辺と頂点を取り出し、複数回通った辺はその分、多重化したグラフを $G_W$ とする
- $G_W$ は、オイラーグラフ(全ての頂点の入次数と出次数が等しい)となる
- ここから $C$ に使われた辺を取り除いても、$G_C$ もオイラーグラフのため、入出次数の一致は保たれる
- 連結性は保たれないかもしれないが、複数のウォークの集合として表現できる
- 今回は、コストが正か負かのみに注目するため、通過順は変化しても気にしない
条件を満たすウォークを $W$ とし、そこから正の閉路 $C$ を取り除くと、残ったグラフの総コストは負になる。
つまり、どれかのウォークはコストが負にならざるを得ず、負の閉路が存在することになる。
矛盾するので、コスト正の閉路しか無ければ、正解ウォークは作れないことが分かる。
負閉路検出はBellman-Fordで、$O(NM)$ でおこなえる。
正閉路も、コストを逆転させれば同様に求められる。