ABC333 F - Bomb Game 2
問題はこちら
問題概要
解説
先頭の人がどうなるかで場合分けを行います.
- 先頭の人が取り除かれるとき:
番目だった人は取り除かれ,
番目だった人は
番目になる.
- 先頭の人が末尾に移されるとき:
番目だった人は
番目になり,
番目だった人は
番目になる.
したがっては以下のようになります.
のせいで遷移に循環があります.
と置くと
は
の一次式
で表すことができるので,
の昇順に
を持ってDPを行うと
となるため,
となります.今回の制約では
が
になることは無いため正しく求めることができます.時間計算量は
で,空間計算量は
です.
提出プログラム
https://atcoder.jp/contests/abc333/submissions/48609390 (見やすさのため空間計算量O(N^2)で書いてます)感想
競プロ用のツールを作ってみた
この記事は競プロ Advent Calendar 2023 16日目 のために作成されました。
ランダム生成ツール
作ったものはこちらです。https://kanpurin.github.io/RandomGenerator/
使用する場面としては、コンテスト中などで適当なランダムデータが欲しいときなどに自分でランダムケースを作成するのが面倒な場合など?
APIとかはないのでプログラムに組み込んで実験するとかはできません。机上での実験や大きいサイズの入力例などで用いてください。
サーバにデータを送って計算してるわけではないので何度も生成しても大丈夫です。
生成するデータは以下のものになっています。
| 数列 | (下限)以上(上限)以下の整数を一様ランダムに |
| 単調増加数列 | (下限)以上(上限)以下の整数からなる長さ |
| 括弧列 | 長さ |
| ラベル付き木 | 頂点数 |
| 単純グラフ | 頂点数 |
| 単純連結グラフ | 頂点数 |
| 素数 | (下限)以上(上限)以下の素数を一様ランダムに生成 |
生成手法は以下の記事にまとめています。
使い方
- "N M"を先頭に付ける:頂点数N、辺数Mを先頭に付けます。競プロでよく見るデータの与えられ方です。
- Seed値を設定する:通常は毎回ランダムにデータが生成されますが、これをONにするとSeed値ごとに生成されるデータが決まります。
- グラフを描画する:グラフを可視化します。頂点が多いときはONにしないでください。

N, Mに適切な値を入れて生成ボタンを押すと下のtextareaにデータが生成されます。
値が適切でない場合は生成ボタンがクリックできません。
textareaの右上にあるボタンをクリックするとコピーすることもできます。

Mが大きくとも生成を高速に行うため、textareaに表示するデータ量に制限を付けています。
末尾が`...`となっていますが、コピーボタンによりデータ全体をクリップボードにコピーできます。

グラフ可視化ツール
作ったものはこちらです。https://kanpurin.github.io/GraphVisualizer/
使用する場面としては入力例として与えられたグラフがどのような形なのか見たいとき、可視化されたグラフを編集したいときなど。
グラフ可視化アプリは結構ありますが、図とテキストの相互変換アプリは知らないので作りました。図→テキストはリアルタイムに変換します。
機能としては以下のものがあります。
- ネットワークグラフの可視化:テキストデータを可視化します。
- ネットワークグラフの編集:可視化したグラフに頂点や辺を追加します。
- 二次元グラフの可視化:テキストデータを可視化します。
- 二次元グラフの編集:可視化したグラフに点を追加します。
使い方
詳しくはアプリに説明があるので触ってみたほうが早いです。分かりづらい機能として「頂点固定」がありますが、描画されたグラフは物理モデルによってプルプルしながらいい感じにの見た目に収束するのですが、頂点を固定することによりその頂点は動かなくなります。
これにより物理モデルの影響を受けない直線のグラフなどが作れるようになります。

二次元グラフの可視化は格子点をクリックすると点が打てるというだけです。
開発者募集
GraphVisualizerは開発者を募集してます。バグや追加したい機能がかなりあるので手伝ってください。「READMEに従ってローカルでGraphVisualizerを動かすことができる」ならどなたでも構いません。
GitやJavaScriptに関して全然わからないので教えあいながらやっていけると嬉しいです。
参加したい方は何か適当に機能提案やバグ報告などのIssueを立てるか、既にあるIssueにコメントするか、私のTwitterにメッセージ飛ばすか、この記事にコメントするか、この記事のURLをTwitterで投稿して「興味ある/参加したい」など言ってください。
ABC299 G - Minimum Permutation
問題はこちら
問題概要
解説
辞書順最小と言えば,前から貪欲に置けるもののうち最も小さいものを順に並べていく方法が考えられます.今回の問題もその方針で考えていきます.以下のサンプルを例に説明していきます.

出来るだけ小さい数字を先頭に選びたいため,まずは答えとなる部分列の先頭の数字がであるかどうかを考えてみます.
すると,以下の図の色付きの数列から答えとなる部分列を選択する必要が出てきますが,が色付きの部分に含まれていないため
を部分列の先頭として選ぶことはできません.

このように,それを部分列の先頭として選ぶと,条件を満たす部分列を選ぶことができなくなるようなものは選べません.したがって,それを部分列の先頭として選んでも条件を満たす部分列を選ぶことができなくならないようなものの中でもっとも値が小さいものを貪欲に選べば良いです.
ここで,から
の整数それぞれに対して一番後ろに出現するものに色を付けます.

先程の考察から,から
までの中から答えの部分列の先頭要素を選ぶことになります(そうでなければ部分列がすべての数を含むことができない).

ここでは,最小要素のを選び,以降の
を選ぶことができなくします.
次の要素は,先程選んだ要素(青)の直後の要素から「まだ選んでない数それぞれに対して一番後ろに出現するもの(橙)」のうち一番先頭にある要素までの中から選びます.

これを繰り返していくことで辞書順最小の部分列を作ることができます.
必要な操作は
- 「まだ選んでない数それぞれに対して一番後ろに出現するもの」のうち一番先頭にある要素を求める
- 区間内のまだ使用していない値のうち最小要素を求める
- 選んだ要素と同じ値をもつ要素を使用不可にする
です.最初の操作はstd::setを用いれば可能,2番目の操作はSegment Treeを用いれば可能,最後の操作はSegment Treeで使用した要素をとすればよいです.
提出プログラム
https://atcoder.jp/contests/abc299/submissions/41075676感想
ランダム生成まとめ
メモ用なので適当に書く.
1以上M以下の整数からなる長さNの数列
長さ重複あり数列の要素に重複があってもよい場合を考える.
各要素の生成確率を
としてもよい.
重複なし数列の要素が互いに異なる場合を考える.
前から順にまだ使ってない数を等確率で使う.
N個からM個選ぶ組み合わせ
1以上M以下の整数からなる長さNの数列と同じように選ぶ,順番は無視.1以上M以下の整数からなる長さNの単調増加数列
N個からM個選ぶ組み合わせと同じ.総和がMとなる非負整数からなる長さNの数列
括弧列
バランスのとれた括弧列の一様ランダムな生成.いわゆる構成比法で生成する.括弧列は,以下の図の右下から左上への経路と対応するため経路をランダムに生成することを考える.

毎回,上か右を選択して移動するが,どのような確率で選択すればよいか.
それは,「上に行った場合の残りの経路数」と「右に行った場合の残りの経路数」の比率で遷移の確率が決まる.
これを用いて,上を選択する確率を求めると,
となる.ただし,は現在地から右上の点までの上方向の遷移回数で,
は現在地から右上の点までの右方向の遷移回数.
初期値は,長さの括弧列を生成するとして,
であり,
上を選ぶと,右を選ぶと
となる.
二分木
括弧列を
ラベル付き木
prufer列と対応が取れるので1以上M以下の整数からなる長さNの数列を使う.次数列を持つグラフ
与えられた次数列を持つグラフを一様ランダムに生成する.Fast uniform generation of random graphs with given degree sequences
個数上限付き01列
の個数を
と決めるとN個からM個選ぶ組み合わせとして生成できる.
の個数が
となる確率は,
となる.
が小さければそのまま,
が大きければラプラスの定理を用いることで計算できる.
境界はとかでいいかも.
N頂点M辺の連結グラフ
すぐに思いつく方法として,頂点
辺の連結グラフを生成する方法はいくつかある.
生成検査法
この方法は一様ランダムに生成できるが,と
が近い場合,連結となる確率が低いため生成回数が増大する恐れがある.
全域木法
この方法は高速に生成できるが,一様ランダムにはならない.
構成比法
特定の性質を持つグラフの数に応じた確率を用いることで,比較的速く(それでもまだ遅い),一様ランダムに生成できる.構成比近似法
構成比法では構成比の計算に時間がかかっていた.構成比近似法では構成比の近似値を用いることで高速な生成が可能になる.ほぼ一様ランダムになる.構成比近似法
重複上限Kの数列
が小さかったり,
かつ
がある程度大きかったりすると生成検査法で高速に一様ランダムに生成できる.
のときは
から
まで順に,空いているところから
箇所選んで決めていく方法で一様ランダム生成できる.
は使ってない整数から一つ選んで前から埋めていけばよい.
生成検査法で回で条件を満たす確率は,
年が
日であり,
人のうち同じ誕生日の
人組が存在しない確率と同じ.(誕生日のパラドックス)
ラベル無し根付き木
https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=b169f11a617bab9395da9f880ce677ca520b2348https://www2.math.upenn.edu/~wilf/website/CombinatorialAlgorithms.pdf
ラベル無し木
https://www2.math.upenn.edu/~wilf/website/Free%20trees.pdf木の総数の近似
N以下の素数
ミラーラビンを使った生成検査法
素数定理から比較的早く生成できることがわかる
全域木
与えられたグラフの全域木を一様ランダムに生成する.http://jakub.tarnawski.org/slides_soda_2015.pdf
ライブラリ
github.com| rand_lab_free_trees() | ラベル付き木の一様ランダム生成 |
| rand_lab_rooted_trees() | ラベル付け根付き木の一様ランダム生成 |
| rand_ulab_free_trees() | ラベル無し木の一様ランダム生成 |
| rand_ulab_rooted_trees() | ラベル無し根付き木の一様ランダム生成 |
ABC287 G - Balance Update Query
問題はこちら
問題概要
種類目のカードの得点を
に変更
種類目のカードの枚数を
に変更
枚のカードを選ぶときの得点の総和の最大値を求める.
解説
各ノードに部分木の得点の総和とカードの枚数の総和を持つBinary Trieを作ると,各クエリサンプル1を例に挙げて説明します.
初期状態のBinaryTrieは以下のようになります.

最初のクエリでは,枚使った最大の総得点を求めます.
根の右の子の枚数がであることから左の子のカードを使うことはありません.したがって,右の部分木について答えればよいです.
そのさらに右の子を見ると,枚数が枚なので右の子のカードはすべて使い,
として左の子に遷移して再帰的に求めていけばよいです.
2番目のクエリや4番目のクエリのような更新では,更新前の得点や枚数を各ノードから引き,更新後の得点や枚数を足していけばよいです.
提出プログラム
https://atcoder.jp/contests/abc287/submissions/38668500感想
ABC285 E - Work or Rest
問題はこちら
問題概要
曜日- 曜日
が「休日」である場合は
- 曜日
が「平日」のとき,直前の休日が
日前,直後の休日が
日後である場合は
一週間当たりの生産量の最大値を求めよ.
解説
曜日
つまり,曜日は必ず休日としてもよいです.
次に,初日が休日で,その後平日が続くようなブロックに分解してみます.

休日日+平日
日の全
日からなるブロックの生産量は公式解説の
と等しいです.これを
とします.
すると,この問題は
- 品物
の重みを
- 品物
の価値を
- 品物の重みの合計の上限を
としたときの個数制限なしナップサック問題となります.
よって,この問題を時間計算量で解くことができます.
提出プログラム
https://atcoder.jp/contests/abc285/submissions/38167895 C++https://atcoder.jp/contests/abc285/submissions/38169128 Python
N = int(input()) A = list(map(int,input().split())) V = [0] * (N+1) DP = [0] * (N+1) for i in range(2,N+1): V[i] = V[i-1] + A[i//2-1] for j in range(i,N+1): DP[j] = max(DP[j], DP[j-i] + V[i]) print(DP[N])