CPCTF2024 ご参加ありがとうございました!
この記事は 2024/04/20 ~ 2024/04/21 にかけて行われた CPCTF2024 の PPC 分野の作問者 (Nzt3,Sotatsu,cho,ponjuice,kenken) による Writeup になります。
今回の CPCTF では PPC 分野は yukicoder 上で開催されました。まだ問題を見ていないという人はこの Writeup を読む前に解いてみてください!
https://yukicoder.me/contests/493
目次
Newbie
Easy
Medium
Hard
Newbie
About half
作問者: @cho
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
92 | 0 | 0 | 0 | 148 |
問題文
飴が全部で 個あって、Aliceに 個、Bobに 個分けました。
AliceとBobは、相手の持っている飴の個数が、自分の持っている飴の個数の 倍より大きいとき、分け方に文句を言います。
この分け方にAliceもBobも文句を言わないかどうか判定をしてください。
制約
- 入力はすべて整数
解法
か のどちらかが成り立つときNo
を出力し、それ以外ではYes
を出力するとよいです。
作者感想
罠のある簡単枠です。
Compound Word
作問者: @Ponjuice
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
71 | 0 | 0 | 1 | 113 |
問題文
個の文字列 が与えられる。
これらの文字列のうち、異なる二つの文字列 を選び、 の順につなげた文字列を をします。
この時、 としてあり得る文字列は何通りあるか求めてください。
制約
- は整数
- は英小文字からなる文字列
解法
としてありうるものの文字列を全て作りそれが今までにあった文字列と一致するかどうかを調べると良いです。
C++やpythonでいう set という集合型を使用するとより簡潔に実装することができ、高速に動作します。
作者感想
ponjuice
って pon
と juice
をつなげた文字列だなってところから作りました。
の制約を僕が解けなかったので制約は になりました。
Easy
CPC To F
作問者: @Ponjuice
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
38 | 1 | 1 | 0 | 70 |
問題文
C
、P
、T
、F
からなる長さ の文字列 が与えられる。
あなたはこの文字列に対して部分文字列 CPC
を探し、選んだ CPC
を F
に変える力を持っている。
あなたは何回でもこの力を使うことができるとき、連続部分文字列 CPCTF
を最大で何個含ませることができますか。
制約
- は整数
- は長さ の文字列
- は
C
、P
、T
、F
からなる
解法
前から順にCPCTCPCをCPCTFにした時のCPCTFの個数を数えると良いです。
Python で書くとかなり簡潔に答えることができます。
input()
print(input().replace('CPCTCPC','CPCTF').count('CPCTF'))
作者感想
CPCTF っぽい名前の問題名を作りたいなって考えてたらこんな問題になりました。
python の想定解コードを書いたときにここまで短く書けるのかとびっくりしました。
Old Maid
作問者: @Sotatsu
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
35 | 0 | 0 | 0 | 65 |
問題文
を正の偶数とします。
の順列 があります。とらお君は、次の手続きによって の順列 を作ろうとしています。
まず、空の数列 を用意します。 が空になるまで、次の操作を繰り返します。
- の隣り合う つの要素を選び、 とする。 を から取り除き (このとき、 は だけ短くなる)、 をこの順のまま の末尾へ追加する。
が空になったとき、 は の順列になっています。
辞書順で最小の を求めてください。
制約
- は偶数である。
- は の順列である。
解法
操作をそのまま実装すると、最小値を見つける・数列から要素を削除するのにそれぞれ かかり、それを 回繰り返すので全体の計算量が になってしまいます。
例えば、最小値取得は、
std:set
を使う ()- 最小値が単調に増えていくことを利用して、最小値がどれだけ増えるかを愚直に求める (ならし )
数列からの要素の削除は、
- 連結リストを使う ()
- の残っている要素のもともとのindexを
std:set
で管理する ()
などで実装できます。
よって、全体で や でこの問題を解くことができます。
作者感想
ARC080Eの逆です。元の問題より簡単になりました。Young Maidsの公式解説になぜかOld Maidって書いてあるの何でなんですかね。
Balanced Choice
作問者: @Nzt3
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
31 | 0 | 0 | 1 | 57 |
問題文
個の石があります。 番目の石のタイプは であり、重量は 、 価値は です。ここで は か のどちらかです。
あなたは合計の重量が 以下になるようにいくつかの石を選んで箱に入れますが、タイプ の石の総重量とタイプ の石の総重量の差が 以下でなければなりません。
この条件を満たすように石を選ぶとき、価値の総和の最大値を求めてください。
制約
- 入力は全て整数
解法
タイプ の石だけで総重量が となるときの価値の最大値 を について求めておきます。
これはナップサックDPにより可能です。
どのようにタイプ の石を選んでも総重量 が達成できない場合は、 とします。
同様にタイプ の石だけで総重量が となるときの価値の最大値 も について求めておきます。
最終的な答えを求めるには、タイプ の石の総重量と タイプ の石の総重量の組 を全探索します。
を満たすようなもののうち、 が最大となるようなものを選べば良いです。
作者感想
ナップサックDPは「重さW 以下 で達成できる価値の最大値」で書くことが多いのですが、「ちょうど 重さW」で書く必要のある問題もありますね。問題に応じて使い分けましょう。
Time is money
作問者: @cho
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
25 | 0 | 0 | 0 | 48 |
問題文
町が 個あり、 本の道で結ばれています。
番目の道を使うことで町 と町 を双方向に移動することができます。
また、道を使うのに通行料が 円かかり、この道を使っての移動には 時間かかります。
町 では働くことができて、 時間働くごとに 円手に入ります。
道を使っての移動と働くこと以外にかかる時間は とします。
あなたは今町 にいて、所持金は 円です。
町 から町 までなるべく早く移動するとき、町 にたどり着くのは最短で何時間後ですか?
ただし、町 まで移動することができないときは-1
を出力してください。
制約
- 入力はすべて整数
解法
働いたときの時給を円とします。ある道を通るのに 円、 時間かかっているとき、道のコストを と表すことができます。
このコストは、通るのにかかる時間をその時間だけアルバイトしたときのお金に変換したものになります。
よってこのコストを最小化するようにダイクストラ法をおこない、町 にたどり着いたときのコストの最小値を として、 を で切り上げで割ったものが答えになります。
また、各頂点にたどり着くまでに必要な最短時間と残ったお金の2つの値を持ってダイクストラ法をすることでも正答することができます。
作者感想
問題のタイトルが答えになってるタイプの問題を作ろうとして生まれた問題です。
当初、yukicoderでの難易度を星1.5にしていましたが、ダイクストラ法が想定である問題の難易度として適切ではありませんでした、すいません……。コンテスト後に難易度を星2に更新しました。
Medium
Car Flow
作問者: @Sotatsu
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
21 | 0 | 0 | 0 | 38 |
問題文
と のみからなる長さ の数列 が与えられる。
それぞれが長さ の数列 が次のように定義されます。
以下の極限値が有理数に収束することが証明できるのでそれを求めて、 で出力してください。
制約
- 入力はすべて整数
解法
の中の の個数を として答えは、
- のとき、
- のとき、 です。
作者感想
中学生の頃知った車の渋滞シミュレーションをセルオートマトンでやるやつが元ネタです。一次元セルオートマトンのルール184っていうらしいですね。証明が大変でした。
Power! or +1
作問者: @Nzt3
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
14 | 0 | 0 | 0 | 22 |
問題文
初め、 です。操作を 回以上行って を の倍数にするとき、コストの総和の最小値を求めてください。
毎回の操作では次の つの操作のうち つを選んで行います。
- を に置き換える。コストは かかる。
- 正整数 を選択し、 を で置き換える。コストは かかる。
- を に置き換える。コストは かかる。
制約
- 入力は全て整数
解法
を で割ったあまりと、 が 以上であるかどうかを管理すれば良いです。
の状態それぞれについて をその状態にするためにかかる最小のコストを求めれば良いです。これは各状態を頂点とした最短経路問題を解けば良いです。
操作2で選択する は小さいものに限られ、操作3でも実際に階乗を計算する必要があるものは 未満に限られます。
各状態での遷移の数(= 最短経路を求めるグラフの頂点の次数)は であることがわかったため、ダイクストラ法を用いると全体の最短経路問題の時間計算量は となり十分高速です。
作者感想
実はグラフになる問題です。「Power」という感じのめっちゃ強い操作がある問題を生やしたくて生やしました。階乗が冪(power)より強くなってしまったのが残念。
String Swap Battle
作問者: @Nzt3
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
12 | 0 | 0 | 0 | 23 |
問題文
人のプレイヤーがいます。初め、人 は文字列 を持っています。それぞれのプレイヤーは誰がどのような文字列を持っているかを全て知っています。
次の2つの段階からなるゲームを行います。
段階1
- それぞれのプレイヤーが好きな正整数を宣言する
- 宣言された正整数のうち最小のものを とする
段階2
- それぞれのプレイヤー が丁度 回次の操作を行う
- 自分が持っている文字列 の異なる位置の2文字を選択し、入れ替える
最終的に作られた文字列のうち、辞書順で最小の文字列を持っていた人全員が勝ちです。また、勝者は勝者の人数を として 点が得られます。
全員が自身の得点を最大化する戦略をとったとき、全員の得点を求めてください。
制約
- は英小文字のみからなる
解法
は またはそれと同等の結果が得られるような数になります。
のときに誰が勝利するかが分かれば良いです。これを求めるには、それぞれの文字列 について、「異なる位置の2文字を選択し入れ替える」という操作を 回だけ行ってできる辞書順最小の文字列を求めれば良いです。
文字列 に「異なる位置の2文字を選択し入れ替える」という操作を 回だけ行って辞書順最小の文字列を作るとき、選択する2文字の位置は
- swapで辞書順で小さくできるならば、そのうち最も前のもの
- swapで辞書順で小さくできないならば、同じ文字
- swapで辞書順で大きくしかならないならば、影響の小さい末尾2文字
とわかります。これで各プレイヤーの操作後の文字列がわかりました。
この中から辞書順で最も小さい文字列を求め、それを持っているプレイヤーを全て求められれば良いです。これは愚直に行っても間に合います。
作者感想
ゲーム部分はギャグです。出題側はギャグを証明しないといけないのが難しいところ。
文字列部分は丁寧な仕事が求められます。丁寧じゃないやつ絶対落とすケースを頑張って作りました。
Twisted Lattice
作問者: @Nzt3
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
8 | 0 | 0 | 0 | 21 |
問題文
縦 マス、横 マスのマス目があります。
上から 行目、左から 列目のマスを と表記します。
- において、 と は時間 で相互に行き来可能です。
- において、 と は時間 で相互に行き来可能です。
マス目上には 個の出口があり、 個目の出口は にあります。
について次の問題を解いてください。
- 始め出口 にいます。別の出口までの移動にかかる時間のうち、最小のものを求めてください。
制約
- のとき
解法
このグラフ上において から の最短移動時間は で式が変わります。
出口がある列について とその列以外の出口との最短移動時間を前計算しておきます。これは右からの最短移動時間と左からの最短移動時間に分けて計算するとそれぞれ累積minになります。
これによりの出口との最短移動時間は計算ができました。
それ以外の列の出口については、列ごとに出口をmap<int,vector>
などで管理して同じ列の出口を行順にソートしておけば、二分探索で で最短移動時間を計算可能です。
前計算の計算量が になり、各 について で答えを出せるため全体でも となります。
作者感想
螺旋階段上の最短経路問題として作りました。3通りに分けるとまとめて処理できて面白いですね。実装がちょっと重いのが難点。
Hard
Permutation Adjacent Sum
作問者: @Sotatsu
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
9 | 0 | 0 | 0 | 18 |
問題文
整数 , が与えられます。
長さ の順列 に対して、 と定めます。
の順列 通りについて を計算し、その総和を で割ったあまりを求めてください。
制約
- 入力はすべて整数
解法
答えは なので階乗と 乗和 が高速に求まれば、この問題を解くことができます。
階乗 を求めるには、埋め込みを利用します。
具体的には、適当なブロックサイズ を決めて を時間かけて計算し、その結果をソースコードに直接埋め込みます。そして、 を愚直に計算すればいいです。計算量は です。
乗和は、ラグランジュ補間を使います。ラグランジュ補間を用いると、 が既知である 次以下の多項式に対して、 は で計算できます。
乗和 は の 次式であることが示せるので、 を愚直に求めるのに 、ラグランジュ補間で を求めるのに で計算できます。
作者感想
式変形もそんなに難しくはないので、高度典型知っていますか問題なのかもしれない。固定modじゃなくもできるけど、埋め込みがCTFっぽくて良いかなと思って固定modにしました。
Bicolor Pyramid
作問者: @Nzt3
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
6 | 0 | 0 | 0 | 15 |
問題文
赤のブロックが 個、青のブロックが 個あります。これら 個のブロックを使ってピラミッドを作ります。
ピラミッドの 段目は、同じ色のブロック 個を使用して作ります。
最大で何段のピラミッドを作れますか?
制約
- 入力は全て整数
解法
対称性より、 としても問題ありません。そうでない場合は と を入れ替えてください。
のとき
「 段目から 段目までを作るときに青のブロックを 個消費するような配置が可能か」というものをDPを用いて 段目まで求めます。このとき、 段目までを作るときに可能な青のブロックの消費数の最大値を とします。
答えは を満たす最大の整数 です。
のとき
答えは を満たす最大の整数 です。
個のブロックを全て使い切るように配置すると自明な上界が達成可能なので、以上の数は必ず異なる平方数の和で表せることを用いるとのときに自明な上界が達成可能であることが示せます。全探索を回すとなら自明な上界が達成可能であることまで示せます。
作者感想
謎定数により全てが解決する問題です。異なる平方数の和として表せない数が有限個で最大値が小さければ解けるな〜と思って調べたら解けてしまいました。びっくり。
四平方和定理とかからエスパーできるので証明難易度に対して多く通されましたね。
Strange Clock
作問者: @Nzt3
AC者数
ヒントなし | ヒント1 | ヒント2 | ヒント3 | yukicoder |
---|---|---|---|---|
3 | 0 | 0 | 0 | 7 |
問題文
を次のようなタイマーとします。
- : 進数を 桁表示するタイマー
- : 進数を 桁表示するタイマー
- : 進数を 桁表示するタイマー
これらのタイマーは、全て次の表示方法に従います。
- スタート時、 の表示は少なくとも1つは違っていた。
- タイマーは、スタートした後表示される数が 秒ごとに ずつ増えていき、 桁で表示できる最大の数が表示された 秒後に表示が になる。
- タイマーは表示が になった後も 秒ごとに ずつ増えていくという動作を繰り返す。
スタートから 秒後に初めて の全ての表示が一致しました。
スタート時の表示としてありうるものは何通りありますか。
制約
- 入力は全て整数
解法
秒後に一致した表示として考えられるものは 通り存在します。
- の表示を3進法で解釈した数値を
- の表示を4進法で解釈した数値を
- の表示を6進法で解釈した数値を
とします。
を とすると、3つのタイマーがある表示 で一致する時刻 は 合同方程式
の解として得られます。
合同方程式は中国剰余定理が使える形になっており、拡張ユークリッドの互助法などで高速に解けます。
この解 を と での分類ごとに分けてソートすると、各分類での表示が一致する時刻の一覧が得られます。あとは直前に一致する時刻との間隔が 秒より長くなるような表示を数えれば良いです。
計算量は です。
しかし、これを実行時間制限に間に合わせることは結構難しいです。
ここで、出力を観察すると「表示が一致する時刻の間隔の種類数は少ない」ということが分かります。
それぞれの について間隔としてありうる値とその間隔を持つ表示の数を計算し、コードに埋め込むことで高速に答えを求めることができます。
作者感想
新入生向けに共通テストモチーフの問題を作ろうとしたらボス問が生えました。定数倍バトルは罠のつもりだったんですが、勝利している人が(Tester含み)何人かいてすごいですね。
おわりに
本当にたくさんの人に参加していただいたようです。ありがとうございます。
30時間という競プロにしては長時間のコンテストでしたが、PPCは問題不備なく終えることができました。
難易度評価もそこまでずれていないようでよかったです。
また、来年のCPCTFでお会いしましょう。