ABC050 D - Xor Sum
問題
提出コード
方針がたってから遷移をバグなく求めるまでになんと半日かかりました!
解法
桁DPです。
というのも、2進数になおした2つの数字のペアについて、それぞれの桁についてありうる桁の組み合わせは、
の3種類のみです。
そして、これらの組み合わせが、そのままの組み合わせになります。
については、とではどちらも0になりますが、のパターンは、をしたときにくりあがりをするため、とは値が必ず異なります。
ということで、の3種類の組み合わせをのそれぞれの桁に当てはめていったときに、がを下回るものの個数を求めれば、答えになります(はかならず以下になるので、これ以降はについて考察をする必要はなくなります)。
ということで、の組み合わせの中で、が最大となるのは、のときになるので、を2進数に直した場合の桁数だけ、3種類のビットの組み合わせを当てはめていきます。
とします。
:左から今桁目を見ている、という情報です。
:桁目でくりあがりが行われたかどうか、という情報です。真なら1、偽なら0です。
:今作成しているは、となることが確定しているかどうか、という情報です。真なら1、偽なら0です。
初期状態は、
となります。これはそれぞれ、桁目でを選んだパターン(桁目の繰り上がりなし)、桁目でを選んだパターン(桁目の繰り上がりなし)、桁目でを選んだパターン(桁目の繰り上がりあり)、の3つのパターンに対応します。
ここからは、0-indexedで考えていきます。例えば、2進数で110という数字は、左から0桁目と1桁目が'1'、2桁目が'0'です。
遷移の個数がかなり多いです。
の桁目が0か1か
の桁目の組み合わせをどれにするか
桁目の時点でがどうだったか
桁目のがどうなるか
によって遷移が決まります。の桁目が0か1か、についてまずは考えていきます。
の桁目が'1'
の時点で、が1ならばでもはそのままになります。が0のとき、の桁目が'0'になるような計算をしたときには1に変化し、'1'になるような計算をした場合はは0のままです。の桁目が'0'
の時点で、が1ならばでもはそのままになります。
が0のとき、の桁目が'0'になるような計算をしたときには0のままですが、'1'になるような計算をした場合はそもそもを超えてしまうので、遷移ができません。
ということで、の桁目が'0'になるか'1'になるかで、遷移が変わってくることがわかります。あとは、その他の遷移にかかわってくる条件をまとめて場合分けし、の桁目がどうなるかを調べれば、遷移が確定します。
- 3つの遷移に関わる条件について考え、の桁目がどうなるかを調べる
として、場合分けをしていきます。
の桁目
というように表記し、羅列していきます。は、そのようになる可能性がないことを示します。
です。あとは、これらとの桁目による遷移の条件をもとに、ひたすら羅列をしていくことで、答えを求めることができます。
codeFlyer (bitFlyer Programming Contest)予選 C - 徒歩圏内
問題
提出コード
これを自力で解けなかったのは結構悔しいです…最近思い通りになかなか解けないですね
解法
,,となるようなの組を探します。
要素が3つ存在するので、まずは真ん中を決め打ちしたときののペアの個数を求めることを考えます。
あるについて、となるようなおよびとなるようなの個数は、それぞれ二分探索で求めることができます。
の個数
を満たすことから、式変形をするとを満たす必要があります。ということで、までで、上の条件をみたすものの個数を調べるため、
から、となるの個数を引けばよいです。
これは、二分探索を利用することで求められます。の個数
を満たすことから、を満たす必要があります。
ということで、以降でこの条件を満たす区間の個数を求めるため、
となるの個数から、を引けばよいです。
ということで、との個数を求められたので、のペアの個数は、それぞれの個数の積になります。
ただし、このままだとを満たさないものが存在しています。
ということで、最後にこうならないものを全体から引けば、答えが求まります。
- となるの組の個数を求める
となるのペアを決めたとき、ありうるは~です。
今回は、を決め打ったときに、上のをみたすのペアの個数を求めていきます。
となるの中で、最も遠いようなをまず探します。
これは、二分探索で求めることができます。これを仮にとします。
すると、のペアは、今求めた~今求めたの中から2つの数字を選ぶ方法と同じになります。
このとき、すべてのについてを満たしています。なぜなら、がこの条件を満たすの中で最も遠い場所になるので、それより後ろの場所をとしても、との距離は小さくなるだけです。
ということで、から、となるの個数をとしたときに、
となるの組の個数は、
となります。
全てのについてこれを求め、最初に数えたものから引いていけば、最終的な答えとなります。
ABC050 C - Lining Up
問題
提出コード
人生初の嘘解法を通しました!!!割と条件を省いて書いたのですが通ってしまったので、サンプルが弱いと思います(ACしない凡例が存在します)。
例えば、自分のコードだと
5 0 0 0 2 2
という入力で答えが6になります(本来、このような入力の場合答えは0になるはずです)
解法
解法自体は正しいです(のはずです)
報告が正しいかどうかを判定したら、あとは答えが一意に決まります。
報告の判定方法は、
Nが偶数
報告全てが奇数になっていて、報告された数字はすべて2つずつ存在しています。
また、報告されるべき数字は1,3,5,7...N-1の奇数です。Nが奇数
報告される数字は偶数になっていて、0を除き報告された数字はすべて2つずつ存在しています。0は1回だけ報告されます。
報告されるべき数字は0,2,4...N-1です。
上の条件を満たさないときは答えが0、満たすときは
です(Nが奇数の場合は切り捨てをします)。
これを求めるだけで答えとなります。
正しい報告かどうかの判定をする部分の条件をかなり緩めにしたのですが、ACをしてしまいました。よくないです。
codeFlyer (bitFlyer Programming Contest)予選 D - ハンコ
解法
基本方針は、いもす法を利用して移動したときに黒くなる部分を求めていくことです。
紙の左上にまずはハンコを合わせます。すると、
マスが黒かったとき、右にマス、下にマスの範囲内がすべて黒く塗られることになります。
ということで、これを1次元いもす法を2回適用、もしくは2次元のいもす法を適用することで黒く塗りつぶされている場所がわかります。
ですが、もちろんが間に合うわけがないので、効率化します。
紙全体を図のように場合分けして考えます。
白い部分
1つ1つの四角は、多くてマスなので、そのまま求めてしまって十分です。オレンジの部分
オレンジの部分は、どちらも列存在します。
元々のハンコの上から行目のマスは、それぞれのオレンジの部分についての上から行目のマスを通過します。ということで、元々のハンコについて、行目で1つでも黒い部分が存在していたら、オレンジの部分についての行目はすべて黒くなり、そうでなければすべて白くなっています。
全てのについて同じことが言えます。ので、オレンジの部分は1列に圧縮することができます。
そして、最後にオレンジの部分を圧縮した1列の個数を、倍することで、個数を数え上げることができます。青の部分
青の部分は、どちらも行存在します。
オレンジの縦横逆バージョンです。なので、1行に圧縮することができ、最後に倍してあげることで、数え上げることができます。黄色の部分
この部分は、縦が行、横が列になっています。
ハンコのマスはすべてこの黄色の部分を少なくとも1度は通ります。
ので、マスのうち1つでも黒い部分が存在していれば、黄色の部分はすべて黒になります。
黄色の部分を1マスに圧縮し、最後に倍してあげると、この部分を数え上げることができます。
ということで、最終的には、
行列の長方形についていもす法によって状態をつくりあげ、最後に圧縮している部分を拡大することで、答えを求めることができます。
AGC024 B - Colorful Slimes
解法
スライムを好きなタイミングで捕まえることができ、捕まえる、魔法を唱える、捕まえる魔法を唱える…といったような順番で操作を行うことができます。ので、魔法を唱えるを唱える回数をまずは固定してしまって、捕まえるスライムの種類を決めてから、最後にを追加します。
これをについて全探索して最小値を求めることで答えを求めていきます。
回魔法を唱えることにすると、回のうち好きな回数変色ができます。
ということで、魔法を唱える回数が決まってしまっているので、番目のスライムを入手したいとき、~のスライムのうち一番入手時間が短いものを選んでくると、番目のスライムを入手するために必要な時間を最小にすることができます。
区間についての最小値を求めたいので、RMQを使ってごり押しをします。
が負になる場合を考慮するために、RMQは要素数をにし、~を2つ並べます。すると、は負になることがない(魔法を唱える回数がより大きくなったときに答えとなることは絶対にありません)ので、配列外を参照することはなくなります。
ということで、あとは全てのについて、区間の最小値をRMQで求めて加算していき、最後にを追加すれば回魔法を唱える場合の最小値になります。
これをについて全探索することで、答えが求まります。
ARC077 D - 11
解法
ダブっている数字は数列に1つ必ず存在します。
ダブってる数字のうち左側にあるものを,右側にあるものをとしたとき、数列は次のようになっています。
ここで、はそれぞれ0個以上の要素を持っている数列です。
長さの部分列を数え上げる方針としては、まず
というを除いたパターンを数え、その後が存在した場合に新しく作成できるパターンを計算して合算します。
を無視した場合
という数列には、~の数字がすべて1つずつ含まれています。ので、全ての数字について、使うか使わないかの2通りを考えればよく、これは
となります。を考慮した場合
新しく増えるパターンは、必ずを使用します。なぜなら、使用しないパターンはすでに前の段階で数えているからです。
ここは、さらに2つに場合分けします。を部分列に含むか含まないか、です。
を含む場合
を使用することが確定しているので、残ったの部分から個を選びます。ということで
となります。を含まない場合
とが同じ数字であるので、被りがないように数え上げなければなりません。
ここで、どのように部分列を選んだらまだ数えられてない部分列(ここで数えるべき部分列)になるかというと、
の中から少なくとも1つの要素を選んだ場合
です。
の要素は、を使用した場合でもを使用した場合でも、その数字より左側に、はどちらの場合でも右側に存在します。
ですが、にある要素は、を使用した場合はの右側に、を使用した場合ではの左側に並ぶことになりますので、かならずまだ数えられてない、新しい部分列を作成できます。
の中から少なくとも1つの要素を選ぶことについて考えると、
全て使うパターン、を使うパターン、を使うパターン、のみ使うパターンの4通りです。
これをそれぞれ求めるのは難しいので、の中から自由に選ぶパターンから、余分なパターンを引く、包除原理の考え方を使うことにします。
の中から自由に選ぶパターンの計算式をたてます。すでに決まっているのはのみなので、から残りの個を選択します。の個数をその文字で表現したとき、
となります。
そして、余分なパターンは、を使用しないパターンです。これは言い換えると、のみから選んだパターン、となります。
から自由に選ぶパターンは、簡単に計算することができ、
です。あとは、上の式から下の式を引くだけでよく、
となります。
ということで、最終的な式は次のようになります。
あとは、それぞれのについてこれを求めれば、答えとなります。
ABC018 C - 菱型カウント
解法
ある地点で×な部分があると、その×を中心として作成したひし形の範囲内の任意のマス(x,y)について、(x,y)を中心とするひし形は作成できません。
ということで、
memo[i][j] = (i,j)を中心としてひし形を作成したとき、その範囲内に存在する×印の個数
とすると、
ということで、全ての×印のマスについて、その範囲内のマス目(x,y)についてmemo[x][[y]に1追加すれば、memoが完成します。
あとは、中心としてありうる(i,j)について、memo[i][j]=0となっているマス目を調べればよいです。
ここで、単純にmemo[x][y]に1を追加していたのでは間に合わないので、いもす法を用います。
(x,y)が×印だったとき、
memo[i + l][max(0, j - k + 1 + l)]には1を追加、memo[i + l][min(c, j + k - l)]からは1を減らす、という操作をしていけばよいです。
ここで、lは0~k-1になります。
あとは、横方向について累積和をとると、memoを完成させることができます。