AtCoder Regular Contest 184 A,B問題メモ
A - Appraiser
問題文
- この問題は インタラクティブ な問題であり、ジャッジは適応的(adaptive)です。
- 適応的: ジャッジは、これまでの回答に矛盾しない範囲で自由に答えを変更できる。
- (=一意に確定していないまま解答するとWAの可能性がある)
- 問題文中のパラメータは $N=1000,M=10,Q=950$ で固定されています。
- 硬貨が $N$ 枚あり、 $1,2,\dots,N$ の番号が付けられています。これらの硬貨のうち、丁度 $M$ 枚が偽物です。
- 鑑定士は $1$ 度の鑑定で $2$ つの硬貨が同種か異種かを判定できます。厳密には、
- $2$ つの硬貨が「双方とも本物」「双方とも偽物」のどちらかであれば、同種と判定する。
- そうでないとき、異種と判定する。
- $Q$ 回以下の鑑定で、全ての偽物の硬貨を特定してください。
制約
- $N = 1000$
- $M = 10$
- $Q = 950$
解法
論理パズルでありそうな問題。
偽物は10個しか無いので、11個以上の同種があったら必ず本物。
例えば「硬貨1と、2~1000をそれぞれ比較する」と、同種となる10個がわかる。ただし999回かかるのでWA。
これは、グループ分けすることで回数が多少節約できて、20個ずつの50グループに分けると、
- グループ1: 硬貨1と、2~20を比較する。
- グループ2: 硬貨21と、22~40を比較する。
- …
として、各グループにおいて少数派の方が偽物とわかる。
1グループ19回の判定で、50グループ950回に収まる。
ただし問題は、「1グループの中に偽物が全て存在し、10 vs 10 になってしまう」ことがある。
この場合のみ、少数派がどちらか分からず、グループに対して追加の調査が必要になる。
仮に21個ずつのグループに分けるとこういう問題は発生しなくなるが、最悪回数が950回を超えてしまう。
場合分けする。
- グループ1~49の中で、10vs10グループが見つかったとき
- 少なくとも操作回数はグループ50の19回分が残っている。
- 10vs10グループ以外の硬貨は全て本物。1枚持ってきて、10vs10グループの1枚($x$)と比較する。
- 同種なら、$x$ と同種のものが本物。異種なら、$x$ と違う方が本物。
- グループ1~49が、全て本物だったとき
- グループ50は、判定するまでもなく10vs10グループ確定。操作回数は19回残っている。
- グループ50以外から本物と確定した硬貨を1枚持ってきて、グループ50の19枚と比較する
- 異種と判定されたのが偽物。
- 偽物が9枚しか見つからなければ、判定してない20枚目も偽物。
これで、950回以内に判定できる。
解法2
(ACの上では不要だが)より回数が少なく済む方法。
この問題のジャッジとしてどんな判定プログラムを書けば良いかを考えると、以下の方法が思いつく。
- 硬貨を頂点、問われた2硬貨間の情報を(同種異種問わず)辺として扱う。
- 連結になった頂点は、(今度は同種異種を考慮して)2つのグループに分けられる。
- $i$ 番目の連結成分の2グループの頂点数を、$X_i,Y_i$ とする。
- 以下のDPをおこなう
- 各 $i$ から $X_i,Y_i$ のいずれかを選んで、合計をちょうど○○にする方法の個数を求める
- $10$ にする方法が1通りか判定する。
- (※最初に仮定した想定解通りに質問に回答していれば、必ず1通りは存在する)
「10にする方法が1通り」という条件を満たせる中で、なるべく辺数を減らしたい。
連結成分は木であるのが一番無駄がない。
さらに連結成分の個数は多い方がよい。(グラフが森の時、辺数=頂点数-連結成分数)
例えばある $i$ で $X_i \ge 11$ だと、選べるのは $Y_i$ に一意に決まる。
全ての $i$ で片方のグループが11個となるように森を構成すると、全ての選び方が一意に決まるので条件を満たす。
本物は990枚あるので、木はちょうど90個できる。
偽物は、いずれかの木にひっついている形となる。(または、処理の順番によっては未処理で単独の頂点として残っているか)
この時の辺数を考えると、910本となる。よって、910回の質問で答えを確定できる。
具体的な構築方法は冒頭の解説記事参照。
もしかしたら更に突き詰めることはできるかもしれないが、クエリの回答による偶発的な場合分けが必要になりそう。
B - 123 Set
問題文
- 正整数 $N$ が与えられます。空集合 $S$ があり、あなたは以下の操作を何回でも行うことができます。
- 正整数 $x$ を自由に選ぶ。$x, 2x, 3x$ それぞれについて、もし $S$ に含まれなければ追加する。
- $\{1, 2, \dots ,N\} \subseteq S$ を満たすまでに必要な操作回数の最小値を求めてください。
制約
- $1 \leq N \leq 10^{9}$
- 実行制限時間: 8秒
解法
少しずるい埋め込み解法。
追加する数がダブってしまうことを、なるべく減らしたい。
ダブる心配があるのは、$x=2^a3^by$ と(中途半端な)素因数分解をして、$y$ を同じくする数同士のみとなる。
また、そのような数たちを $y$ で割ってやると、その構造はどれも同じである。
■ 2^a * 3^b * y と表せる数 y 1: 1, 2, 3, 4, 6, 8, 9, 12, ... を効率よく追加する方法 5: 5, 10, 15, 20, 30, 40, 45, 60, ... を効率よく追加する方法 7: 7, 14, 21, 28, 42, 56, 63, 84, ... を効率よく追加する方法 「何番目に小さい数まで追加すればよいか(何番目でNを超えるか)」は y ごとに異なるものの、仮にそれが同じなら、必要な操作回数も同じ! ↓ 前計算できそう。
以下の関数を定義する。
- $F(M):=M$ 以下の全ての「$2^a3^b$ と表せる数」を $S$ に追加するのに必要な操作回数
これが仮に求まっているとすると、たとえば $N=25$ のとき、
- $y=1$ のとき、$F(25)$
- $y=5$ のとき、$x=2^a3^b5$ が $N$ を超えない、つまり $F(N/y)=F(5)$ までを追加すれば良い
- $y=7$ のとき、$F(25/7)=F(3)$
- $y=11$ のとき、$F(25/11)=F(2)$
- …
というのを、$y \le N$ まで続けていけば、その総和が答えとなる。
$y$ の取り得る値は「2の倍数でも3の倍数でもない数」、 要は「6で割って1または5余る数」なので数が多く、1つ1つ求めていてはTLEだが、 任意の $[l,r)$ 区間の中にいくつあるかはすぐに計算できる。
$y \ge \sqrt{N}$ あたりを境目に $N/y$ の値が同じものが増えるので、 それらをまとめれば、考慮するのは $2\sqrt{N}$ 個程度の区間で済む。
$F(M)$ を求める
で、残る問題は、$F(M)$ をどうやって求めるかとなる。
$M \le 10^9$ の範囲で $2^a3^b$ と表せる数は300個程度なので、 事前に時間をかけてでもそれぞれの解を求めてしまえば、コードに埋め込むことは十分可能。
問題を少し言い換えると、以下のような、↓に進むと2倍、→に進むと3倍になる表において、
1 3 9 27 81 M=108 として、108以下の値のみからなる表。 2 6 18 54 4 12 36 108 ■■ というスタンプを何個押せば、 8 24 72 ■ 16 48 全てのマスにスタンプを押した状態にできますか? 32 96 というのが、F(108) に相当する。 64
段を下がる毎に1列減ったり減らなかったり形が不規則なので、貪欲法だと埋め方が見えづらい。
DPをする。
- $\mathrm{DP}[i,S]=i$ 行目より下は全て埋め、$i$ 行目で既に押された列の集合が $S$ である場合の、最小操作回数
- $i$ が大きい方から埋めていく
遷移を考えると、下から考えた方が「押されて無い列には必ず押さなければならない」ことが確定するので 枝刈りしやすいかなと思ってこのようなDPにしたが、まぁ、上から($i$ が小さい方から)でも別にいいと思う。
シンプルに、遷移で「次にどこに置くか」を全探索する実装をすると、
状態数が $\log_2{M} \times 2^{\log_3{M}}$、遷移が $2^{\log_3{M}}$ なので、なかなか重い。
まぁ、$i$ が大きいうちは2の指数も小さく、定数倍で何分の一かになる。
また、必ず置かなければならない場所には置くなど適当に枝刈りしつつ実装すると、数分で全て計算できた。
ただ、この解法では、埋め込まずに制限時間内に計算するとなると
($N$ に対して必要な $F(M)$ のみ求めれば良いとしても)8秒だと厳しいか。
DPをさらに高速化する必要がある。