ABC235 参加記録
コンテスト中AC:A〜E
C - The Kth Time Query
二次元配列を用意し、には、が出現したインデックスを順に記録します。
クエリが与えられた時、まず、のサイズがより小さい場合(出現数が)は、-1を出力します。
そうでなければ、を答えとすれば良いです(0-indexedであれば、となることに気をつける)。
しかし、今回が大きく、配列に収まりません。そこで、を座標圧縮します。
同様にも圧縮後の値を得ますが、がそもそもに一回も出現していないパターンがあります。
この場合は、座標圧縮後の値が見つからず、正しく動作しません。このパターンで必ず-1を出力するために、に出現した整数かどうかを記録する連想配列を用意して対処しました。
提出コード
D - Multiply and Rotate
可能な操作を整理します。整数からは以下の条件を満たす整数が作れます。
- の10進表記をとすると、 (但し、の場合)
ある整数にしたいような問題では、から出発して値を小さくしていく、メモ化再帰が使われる場合があります。
しかし、今回の場合、操作によって必ず小さい数になるとは限らないので、この方法は使えません。
そこで、メモ化再帰、つまりDFS木の発想から、操作を重み1の有向辺とみなしてみます。
すると、整数を始点とし、始点から最短経路問題と同様に最小の操作回数を確定できます。
すなわち、現在作ることが可能な整数の中で操作回数が最小な整数は、その後どのように整数を作っていってもその操作回数未満にすることはできません。
以上より、ダイクストラ法でこの問題を解くことができます。
なお、2個目操作で自分より小さい整数にできるため、を超えた整数も考慮する必要があります。
桁数の増減は無いので、が桁なら桁までの全ての整数を考慮しておけば良いです。
提出コード
E - MST + 1
- 辺のコストを昇順に並べて、辺のコストが小さい順から調べていき、辺[e=(u,v)]について、が非連結ならその辺を採用する
というものです。よって、クエリの辺が採用されるか否かはの辺のうち、コストがより小さい辺のみによって決定します。そこで、
- の最小全域木を求める過程で、よりコストが小さい辺を追加した後に、辺が採用されるかを判定する
という方法ではどうでしょうか。
今回の制約上、
- [texG]の辺のコストは相異なり、かつの辺のコストと、クエリの辺のコストは全て異なる
となっています。従って、
- の辺をコストの昇順に並べたとき、並び方は一意(クラスカル法による全域木及び辺の走査順が一意)
- の辺をコストの昇順に並べたとき、クエリの辺の挿入位置は一意
が成立します。これによって、前述した判定方法で達成可能ということが言えます(多分)。
逆に、コストが同じ辺が存在する場合、同一コストの辺の走査順を変えることによって、異なる全域木(又はその部分集合)が構成されてしまい、構成されたものよって辺の採用の要否が異なる可能性が出てきます。
実際の判定方法のアルゴリズムですが、の辺とクエリの辺をごちゃ混ぜにして昇順ソートし、クラスカル法で最小全域木を構成していきます。その過程で、クエリの辺が出てきた場合は、辺の採用の要否のみを判定し、全域木には追加しないようにします。
ABC216G 01Sequence
解説AC
牛ゲーとのことで、解法は公式解説を見るのが良いかと思います。
ここでは、自分なりの理解をつけ足したいと思います。
牛ゲーについては、別記事にまとめました。
また、ネットで調べると色々出てきます。
牛ゲーを適用するには、以下を満たす必要があります。
- 2値の差分の制約しかない
- 値を最大化する
そこで今回、
- 1の数を最小化する
ことを、
- 0の数を最大化する
と言い換えをすることが1つポイントだと思います。
次に、以下の3つが今回の制約です。
制約1は自明かなと思います。
制約3は、0の数を最大化する問題とみなし、1の個数の制約を0の個数の制約に変換することで導き出されます。
制約2ですが、を1にすればとなり、を0にすればとなります。そこで、多重辺をもつような場合はどうなるのかという疑問が出ました。
おそらくですが、「0以下または1以下」なので大きい方の1以下のみが採用されます。これが、「0以下かつ1以下」であれば、0以下が採用されると思います。
牛ゲーでは、
という制約を「からへの重みの有向辺」
とみなします。これに従って、3つの制約式から有効辺を追加し、を始点とした最短経路問題を解きます。重みが非負なので、ダイクストラ法が適用できます。
あとは、について、
- なら、そうでなければ、
としてを構築できます。
牛ゲー
2022/01/15 修正 ・有向グラフの図追加
初めに
AtCoderの問題で牛ゲーが出たので、まとめておきます。数学的に厳密でない部分や、誤りを含む場合もありますのでご了承ください。
牛ゲーとは
以下の2つの問題があります。
という「2つの値の差分についての制約」のみからなる制約の元で、と各の差を最大化する線形計画問題
重みつき有向グラフについて、頂点から頂点にコストの有向辺があることをと表現する。 という辺がある重みつき有向グラフについて、始点から各頂点への最小コストを求める最短経路問題
この2つから得られるの最大値と、始点から頂点への最小コストが同じになります。
従って、
- 2つの値の差分についての制約のみからなる
- 値を最大化する
という条件を満たす問題は、最短経路問題に帰着して解くことができます。
計算量は、を含むならBellman-Ford法によって、、非負整数のみならDijkstra法によって、です。
最大値と最短経路
最短経路問題の最小コストが、線形計画問題の最大値を与えるというのがよくわかりませんでした。
この点について考察してみます。
例として、以下の有向辺を持つ有向グラフを考えます。
とし、までが求まっているとして、を求めます。
有向辺を差分の制約とみなします。具体的には、
2から3への重み1の有向辺は、
1から3への重み3の有向辺は、
となります。
よって、条件はかつとなり、最終的に右辺が小さい方の
という条件のみとなります。
この時の右辺は、最短経路問題における最小コストであると言えます。
という制約において、非負整数であれば、
のいずれでも良いわけです。しかし、有向グラフとして考えていることから、が採用されます。
これは、 という制約のなかでの最大値であると言えます。
もう少し一般化してみます。
頂点が終点となる有向辺について、その有向辺の始点となる頂点をとし、その有向辺のコストをそれぞれとします。
これを線形計画の制約とみなしたとき、 となります。右辺は最短経路問題における最小コストの計算と同じです。
また、最短経路問題と見做しているため、
となります。これは、制約式の最大値となります。
以上が、最短経路が最大値となる理由だろうと思います。
具体例
ABC234 参加記録
C - Happy New Year!
非負整数の2進数表記の1を2に変換する操作を考えます。操作後の値をとします。すると、以下が言えます。
- とは一対一対応する。
- 非負整数の変換によって、0,2のみからなる整数の全てを得られる。
- ならである。
以上から、の2進数表記の1を2にすると答えを得られます。
提出コード
D - Prefix K-th Max
ウェーブレット行列が使えます。
ウェーブレット行列では、以下の関数がで使えます。(はの要素の最大値で、今回は)
- :半開区間の番目に小さい値を返す。
を0-indexedとすれば、について、で答えが計算できます。
計算量は、のウェーブレット行列の構築がボトルネックとなり、です。
提出コード
E - Arithmetic Number
整数の桁数をとします。
候補となる等差数の制約を考えてみます。
- 桁数は
- 公差は
- 最上位桁は、
更に、実際にを構築する時に、回の計算が必要になります。
ワーストケースのおおよその計算量は、
となり、十分に小さいので、全探索可能です。
の各桁の計算の過程で、桁の数がまたはとならないものだけを答えの候補とします。その中で、を満たすもののうち、最小のが答えです。
なお、条件1は桁の整数について、必ずゾロ目が含まれ、これは公差0の等差数になることより導けます。
提出コード
ABC233F Swap and Sort
解説AC。大体の解法は自分が想定したものと合っていました。
ここでは、を、頂点iに書かれた整数と表現します。
「i番目とj番目が交換可能」という条件を「頂点iと頂点jに無向辺がある」と言い換えます。
昇順に並び替えるとは、
- すべての頂点について、とが等しい
と同値です。上記を達成するためには、
- すべての連結成分について、同一な連結成分に属する頂点番号の集合とその頂点に番号に書かれたの集合が一致する。
となります。
一見ややこしいですが、すべてのについて辺を辿ってとなる位置に移動できるということを示しています。
次に、操作回数Kの制約があるため、操作回数のワーストを考えます。
頂点が5個の時を例にすれば、
V -> 1-2-3-4-5 P -> 5-4-3-2-1
上記のように頂点が昇順に連結し、Pが降順となっていた時が、バブルソートの交換回数と同様にワーストになると予想できます。
この時、交換回数は
です。よって、N=1000の時、
となり、Kの制約に適合します。従って、さえ保たれていれば、グラフはどのような形をしていても達成可能だということがわかります。
ここで、各頂点からDFSし、頂点iに本来納まるべき整数を順次持ってくる解法が思いつきます。しかし、各頂点からDFSした場合の計算量はとなり間に合いません。
前述したように、グラフの構造は何でも良いので、全域森にしてしまえばよいです。全域森はKruskul法で構築できます。よって、辺の数は最大でも個となり、計算量が間に合います。
今回、頂点と整数は葉である頂点から順に一致させていく必要があります。この実装でかなりつまづきました。
最終的な実装としては、
- 次数1以下の点をキューに追加する
- キューが空になるまで以下を繰り返す
- キューから頂点を1つ取り出しuとする。
- uからDFSして整数を一致させる。この時辿った辺を操作列に追加する。もし一致できなかったら-1を出力して終了。
- uを決定済みとする。
- u及びuと隣接するすべての頂点の次数を1減らす。の次数が1以下でかつ未決定ならキューに追加する。
という風にしました。最初に葉をキューに追加する時は、次数0も追加しないといけません。なぜなら、
V -> 1-2 3 4 Q -> 2-1 4 3
というケースなどでWAとなります。
別に実装してみましたが、次数を減らした後に頂点を追加する時の条件は、「の次数が1である」のみにしてもよく、整数が決定済みかどうかという条件は省けました。
DFSでは、始点sと頂点uに書かれた整数が等しい場合に、始点sまで順次交換操作を行い、辺番号を操作列に追加します。
bool dfs(int s, int u, int p=-1) { //sは始点である頂点番号、uは現在訪問している頂点番号、pは親の頂点番号 if (P[u]==s) return true; // 始点である頂点sと現在訪問している頂点uに書かれたP[u]が一致したら、trueを返す。 for (auto&& e : g[u]) { //辺を示す構造体{始点from,終点to,番号id,コストcost} int v = e.to; //頂点uの辺eに接続する頂点v int id = e.id; //辺の番号。今回は操作番号に一致。 if (v==p) continue; //親なら無視。木を前提としているため。 if (dfs(s,v,u)) { //始点sと一致する整数が見つかったら、 swap(P[u],P[v]); //書かれた整数を交換する ans.pb(id); //操作列に辺番号を追加 return true; } } return false; }
ABC231 Minimal payments
解説AC
お釣りの問題は過去出題例があります。
過去問では、
を参考にしました。
この解説と公式解説を踏まえ、まずは解法を書きます。
例として、
X = 35
A = {1,4,12}
を考えます。
を円払うために必要な最小の硬貨の枚数
と定義します。
は、1円玉でしかやり取りできません。
・1円硬貨で3円払って32円払う問題とする
・1円硬貨を使わず、おつりとして1円を受け取ることを確定させ、36円を払う問題とする
ことができます。
よって、35円を払うのに必要な最小枚数は、
となります。
同様に、は以下のように計算できます。
そして、というように計算できます。
各硬貨を考慮したときには、
・X以下の最小のの倍数。(ここではとします。)
・X以上の最大のの倍数。(ここではとします。)
の2パターンしか現れません。
そこで、以下のDPを考えます。
j=0のとき、円払うのに必要な硬貨の最小枚数
j=1のとき、円払うのに必要な硬貨の最小枚数
初期値は、
です。
漸化式は、
これを金額が大きい順に計算しておくと、答えは(または。等しくなるはず)です。
「はの倍数である」がよくわからなかったこともあり、数直線で考えてみました。
下記の表はの倍数でいける座標(円硬貨で払える金額)を示したものです。
表を見ると分かるように、の倍数であるという制約により、4円硬貨で支払えない金額は、12円硬貨でも払えません。
したがって、
・で得られる端数は、円以下の硬貨で支払うしかない
ことが言えます。
また、
・DPの過程で、円硬貨で支払う金額、、、、が、の倍数である
ことが保証されます。
この問題は、以下のように数直線上を移動する問題に言い換えができそうです。
0から始まり正の方向に無限に伸びる数直線があり、その数直線上を点0から点Xまで移動することを考える。
だけ、進むか戻ることができるとき、操作回数は最小で何回か。
数直線上を移動する問題においても、
・値の大きい方から移動した方が効率が良い
・Xに最も近いの倍数、すなわちまたはのみに移動すればよく、他は無駄である
・考慮する移動方法は、
・からまたはに行く
・からまたはに行く
ことから、元の問題と同様に解けるかなと思います。
ARC132 参加記録
コンテスト中AC:A,B
A - Permutation Grid
nの制約上、実際に構築することはできません。
このことから、の組みによって一意に定まるような法則性がありそうだと予測します。
白黒がどのように決定するか試して見ると、例えばn = 5では、
- の行と列を全て黒で塗る
- の行と列のまだ塗られていないマスを白で塗る
- の行と列のまだ塗られていないマスを黒で塗る
- の行と列のまだ塗られていないマスを白で塗る
- の行と列のまだ塗られていないマスを黒で塗る
となります。
よく考えてみると、この決定順序はの順列の並び方によらず同じです。加えて、ある1つの順列で決定した塗り方について、行や列を交換して別の順列にしても条件を満たします。
よって、ある一例で色の塗り方を確認すれば十分です。
ここでは、がそれぞれ昇順に並んでいるとして、n = 2〜6まで列挙してみます。
0が白、1が黒です。
01 001 0001 00001 000001 11 011 0011 00011 000011 111 0111 00111 000111 1111 01111 001111 11111 011111 111111
こうすると、階段状に塗られることがわかります。
ここで、以下のような法則があると予想できます。
- マスについて、ならそのマスは白、そうでなければ、黒
これがいずれのnについても成り立つことを数学的に証明はできませんでしたが、おそらく成り立つだろうという予測しました。実際、これでACしました。
提出コード
B - Shift and Reverse
2つの操作を突き詰めると、右または左への巡回シフトのみが可能です。
操作によって、昇順に並び替えることが可能という条件から、数列は、
のいずれかをいくらか巡回シフトした数列だとわかります。
よって、最小な操作回数としてあり得るもの以下に限定されます。
- Pが昇順の巡回シフトである場合
- 1が先頭に来るまで、先頭を末尾に移動する
- ひっくり返して、1が末尾にくるまで先頭を末尾に移動させる。そして、再度ひっくり返す
- Pが降順の巡回シフトである場合
- ひっくり返して、1が先頭に来るまで先頭を末尾に移動させる
- 1が末尾に来るまで先頭を末尾に移動させてから、ひっくり返す
よって、Pが元々昇順であったか、降順であったかに応じて、上記の小さい方を出力すれば良いです。
昇順の判定は、最初に1があるインデックスをposとして、
であるすべてのついて、
が成り立つかどうかで判定できます。