競プロ典型 90 問 002 valid parens の列挙
競プロ典型 90 問 002 の解説のうち、 ngtkana さんのもので紹介されている方法がうまくいく理由を次のように考えた。
以下のことを確かめる。
- 操作後の列は正しいカッコ列である
- 辞書順で操作前と操作後の間に正しいカッコ列は無い
お断り
"("と")"からなる列が正しいカッコ列であることと次のことは同値:
とする。列の左から1文字ずつ読み取り、"("ならば+1、")"ならば-1をに足す。以下の両方が成り立つ。
- 最後の文字を読み終えた時点で
- 最後の文字を読み終えるまで常に
上に出てきたをスコアと呼んだり、の文字目を読み終えた時点のの値を指してと書いたりさせてもらう。 また、列が明らかな場面では下付き文字を省略して単にと書かせてもらう
1.を確かめる
操作の前後で"("と")"の個数は変わらないから、操作前が正しいカッコ列ならば操作後の列も先述の(a)を満たす。
以下、操作後の列が先述の(b)も満たすことを確かめる。
操作前の列で最も右にある"())"の"("の位置を左から文字目と呼ぶことにする。
操作後の列を左から文字目まで、文字目、文字目から終わりまでの三つの部分に分けてスコアが0以上となっていることを確認する。
一つ目の部分
操作前後で左から文字目までは等しいから操作後の列の左から文字目まではスコアが0以上である。
二つ目の部分
操作前の列は先述の(b)を満たすことと、文字目が"())"という並びであることからである。
操作後の列の文字目は")"になっているが、が成り立つ。
三つ目の部分
操作後の列のから終わりまでは"("を左、")"を右に寄せた形である。
"("が連続する間は明らかにスコアが0以上である。
")"が連続する間にスコアが0より小さくなると仮定する。
このとき、の文字目から終わりまでの形からしてとなる。
一方、操作の前後で"("と")"の個数は変わらないからとなるべきで、矛盾がある。
背理法から")"が連続する間にスコアが0より小さくなることはない
2.を確かめる
操作後の列が、文字目が")"な列の中では、辞書順で最も早いことは文字目から終わりまでの形から明らかであり、正しいカッコ列でもある。
後は次のすべてを満たす列がないことがわかればよい:
- より遅い
- 文字目までと等しい
- 正しいカッコ列
(i)と(ii)の二つを満たす列は(iii)を満たさないことを確認する。
(i)をまじめに言い直すと、で , , を満たすものが存在する。ここでなどは列の文字目を表す。
(ii)より
ところでについて最も右の"())"より右にある"("ではスコアが1になる。まじめに書くと次が成り立つ:
かつならば
なぜならばとすると、最後の文字を読み終えた時点でスコアが0になるには文字目より右で")"が2個以上連続しなければならない。
文字目より右で")"が2個以上連続すると、文字目の"())"より右にさらに"())"があることとなり、矛盾する。
背理法から
"("を読んでスコアが1になるなら、その一文字前ではスコアは0である。
以上をまとめるととなり、を左から読むと文字目でスコアは負となるからは正しいカッコ列でない
ABC333 D 解説にある実装例
ABC333 Dの実装例にあった次の行がわからなかったので調べた
ranges::max(forest.groups() | views::transform((auto &&g) { return size(g); }))
質問
- | って何
- views::transform って何
- () {} って何
回答
- パイプ
- range と関数を引数にとり、range の各要素に関数を適用して view を返す関数
- ラムダ式
次の質問
- view::transform が関数だとして、問題の行は関数しか引数にとってない(range をとってない)じゃないか
- range, view って何
回答
-
第1引数に
viewable_range
を受け取ってview
を返すカスタマイゼーションポイントオブジェクトを、Rangeアダプタオブジェクトという。(中略)Rangeアダプタオブジェクト
adaptor
が2つ以上の引数をとる場合、以下の3つの式は等しい。adaptor(range, args...) adaptor(args...)(range) range | adaptor(args...)
[2]
これの3つ目の式が使われている(パイプライン記法というらしい?) - どちらもライブラリ<range>が定義するコンセプト
他の質問
- auto &&g って何
回答
※ auto&& の && は reference collapsing が働くことを意味しているものです
[3]
Reference collapsing と関連する語をひとまず次のように思うことにしました
思ってるだけなので間違えてたらごめんなさいアンド教えてください
Reference collapsing
今回はラムダ式の引数に &&g と書いてあり、引数として rvalue 参照が与えられたときだけ g は rvalue 参照、それ以外(lvalue, rvalue, lvalue 参照, あと何)が与えられたときは g は lvalue 参照になる
もし、今回 &&g と書いてあるところを &g にすれば、引数として何が与えられようと g は lvalue 参照になる
というように参照の参照ができたときにそれが lvalue 参照と rvalue 参照のどっちになるかを決めたルール
lvalue, rvalue
lvalue は名前のついてるオブジェクト(オブジェクトって何)、rvalue は一時オブジェクト
lvalue 参照, rvalue 参照
lvalue 参照は普通(?!)の参照。rvalue 参照は参照してる先を壊していい参照(ある状況で無駄なコピーを避けるのに有用らしい)
&g か &&g か
全部 &&g で生きていく
自分でも何が言いたいかわかっていないスピリチュアルコーナー
これまで範囲 for 文を、何も考えず cpprefjp から丸パクリしてから const auto &e を auto &e にして生きてきた
C++11の範囲for文を使うと以下のように書ける:
std::vector<int> v; for (const auto& e : v) { std::cout << e << std::endl; }
変数宣言には直接コンテナ内の要素の型(上記の例であれば
const int& e
など)を書いても良いし、型推論auto
を使うと、さらに簡潔に書ける。変数宣言にconst参照
const auto& e
を書くとコンテナ内の要素の変更を禁止し、要素のコピーも行わない。参照auto& e
を書くと、コンテナ内の要素を変更できる。実体auto e
を書くと各要素がコピーコンストラクタによってコピーされてからfor文に渡される。
変数宣言 e を変更可能か? コンテナ内の要素を変更可能か? const auto& e No No auto& e Yes Yes auto e Yes No
[4]
Reference collapsing を知ったいま、 &e と書いたら e は常に lvalue 参照となることが分かった。&&e と書いても rvalue 参照が与えられない限り e が rvalue 参照になることがないことも分かった。具体的にどんな状況か今の自分には想像できないが、絶対 lvalue 参照にしたいというときに &e を使うんでしょうか。範囲 for 文をするときは範囲をぶっ壊されたらいやだから何かの間違いで rvalue 参照になったらだめそう(だからauto &e と書くんでしょうか)。auto &&e: (ここに範囲に相当するオブジェクトを返す関数呼び出しを書く) とかやったら rvalue 参照になっちゃうんでしょうか。もうわからない
この記事は、自分が件の実装例にある一行を見たときにどのように調べたらよいかすらわからなかったので、関係ある語を列挙しただけのものです。ラムダ式やら view やら lvalue 参照やらについて厳密にご紹介しようという志はありません。
??? 「クネクネとC++について調べる時間で問題解いた方がレート上がりそう(おれのレートがクネクネとC++について調べるまでもない程度に低そうなため)」
参考
[1] AtCoder の記事"AtCoder Library (ACL)"から"document_ja/dsu.md"
[2] cpprefjp - C++日本語リファレンス の"リファレンス/<ranges>", [4] cpprefjp の "言語機能/C++11/範囲for文
[3] プログラミングの教科書を置いておくところ の"C++ 11 auto の使い方"、その他の関連する記事
ARC154 A Swap Digit 解説
解説が間違っているのではないかと思い、この記事を書くことにした。
問題文はこちら。
https://atcoder.jp/contests/arc154/tasks/arc154_a
ここで、とのどちらが小さいかを考えます。これは、の大小関係との大小関係が一致しているのであれば前者、そうでないならば後者となります。
解説に書かれているこの文自体は正しい。しかしを小さくするために、ある桁で交換するかしないか決めたいならば(交換前)と(どちらか一方の桁を交換後)のどちらが小さいかを考えることになるのではないか。これらの大小関係を調べる。
以上よりペアとペアの大小関係が一致しているとき、そうでないときの方が小さい(つまりペアとペアの大小関係が一致しているならば交換しない方がよい)。こうなると次が納得できる。
よって、のうち小さい方をにし、大きい方をにすることによってを最小化できます。
(小さい方をにし、大きい方をにすることによってもを最小化できるはず)
また、解説放送その他で紹介されている以下のやり方の方が手っ取り早い気はする:ある桁を交換してもは保たれるからとする(は定数)。このとき
ということで、は(すなわちの平均)より離れているほどは小さくなる(このことから逆に積を大きくしたいならが同じ大きさになるように寄せるとよいことがわかる)。
これを書いた動機
解説が理解できなくて数日間悩んでから、解説のある個所を変更することで納得のいく文章になったので、その箇所は書き間違いではないかと思った。適当にインターネットで検索した限りだと解説に言及している人や上で書いたようなことを言っている人が見つからなかった。友人(1人)に、具体的な個所は伏せて、間違いがあると思っているが君はどう思うかと伝えたうえで見てもらったところ彼も自分と同じ考えに至った。それで自分だけの勘違いでもなさそうだと思いここに書くことにした。もともと解説が正しく自分が理解できていない、すでに指摘している人がいる、上で自分が書いてることに間違いがあるなど、なんでもあれば知らせていただきたい。