−目次
AtCoder Beginner Contest 356 D,E,F,G問題メモ
D - Masked Popcount
問題文
- 整数 N,M が与えられるので、 N∑k=0 popcount(k&M) を 998244353 で割った余りを求めてください。
- ただし、 & はビット単位 AND 演算を表します。
制約
- N は 0 以上 260 未満の整数
- M は 0 以上 260 未満の整数
解法
2進数で桁毎に考える。
M で'1'が立っている桁において、 k でも'1'が立っているような 0≤k≤N の全ての k は、1ずつ答えに寄与する。
M 100101 ..??1????? ① 0≦k≦N でこんな k の個数 ..?????1?? ② 0≦k≦N でこんな k の個数 ..???????1 ③ 0≦k≦N でこんな k の個数 答え=①+②+③ (※重複していてもよい、たとえば 100100 は①と②で2回数える)
なので、他の桁のことは気にせず、「d 桁目に1が立つような、0≤k≤N となる k は何個ある?」が独立に求められればいい。
a) d 桁目より上位の桁で、既に k<N が確定している場合 d N 110110100 k ????1???? 前半の????の列に来るのは、0000~1100 の13通り。(N を d だけ右bitshiftした値) それぞれに対し、後半の????の列には何を置いてもよいので0000~1111の16通りの数が来る。 → 13 * 16 = 208 通り b) d 桁目では k<N は確定してない場合 d N 110110100 k 11011???? d以上の桁はNと一致している。そのうえで k の d 桁目が1なので、 まず、N の d 桁目も 1 である必要がある。 あとは、それより下位の桁でNを超えないような k を数えればよい。 Nの下位d-1桁(????部分)を取り出した値 + 1(0を含むため)が答えとなる。 → 0100 + 1 = 5通り
これを、各桁について合計したものが、最終的な答えとなる。
E - Max/Min
問題文
- 長さ N の数列 A=(A1,…,AN) が与えられます。
- N−1∑i=1N∑j=i+1⌊max を求めてください。
- ただし、\lfloor x \rfloor は x 以下の最大の整数を表します。例えば、\lfloor 3.14 \rfloor=3、\lfloor 2 \rfloor=2 です。
制約
- 2 \leq N \leq 2\times 10^5
- 1 \leq A_i \leq 10^6
解法
A_i が大きい方(分子)になるような全ての (j,i) ペアの答えを求めたい。(ひとまず、A_i は A の中に1つしか無いとする)
すると、答えは以下のような範囲の合計となる。
例 A = (1 2 2 2 5 5 15 22) Ai=22 について考える 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 個数 1 3 2 1 商1以上 |1--3--------2-----------------------------1---------------------| 商2以上 |1--3--------2------------------| 商3以上 |1--3--------2------| 商4以上 |1--3--------2| 商5以上 |1--3------| 商6以上 |1--3---| 商7以上 |1--3---| 商8以上 |1--3| : 商11以上|1--3| 商12以上|1| : 商22以上|1| 22が分子に来るようなペアにおける商の合計: 1*22 + 3*11 + 2*4 + 1*1 = 64
たとえば商が4となる A_j=5 は、ちゃんと「商1以上~商4以上」の4回でそれぞれ足されている。A の中に2個あるので、2個ずつ足されている。
商を1段ずつに分解していると考える。
これ、商が小さいうちは、横に見て累積和で管理した方が効率がよい。
一方、商が大きい時は、対象範囲が同じものが続くので、縦にまとめて合計した方がよい。
なので、r = \lfloor \sqrt{A_i} \rfloor として、商 r までは横に、以降の部分は縦に見るようにすると、 A_i あたり計算量 O(r) で答えが求められる。
以下を前計算しておくと、
- \mathrm{CNT}[i]=A の中の i の個数
- \mathrm{ACC}[i]= ↑の累積和
k=1~r についてそれぞれ横と縦で足し合わせて、重複する長方形部分を引けばよい。
- 横に合計: \mathrm{ACC}[A_i // k]
- 縦に合計: \mathrm{CNT}[k] \times (A_i // k)
- 重複部分: \mathrm{ACC}[r] \times r
O(N \sqrt{A_{\max}}) となる。
なお、A に同じ値が複数ある場合は、余分なペアを除く必要がある。
同じ値が c 個あるような A_i それぞれで、\frac{c(c+1)}{2} だけ余分に足されている。
F - Distance Component Size Query
問題文
- 整数 K が与えられます。はじめ空である集合 S に対して、次の 2 種類のクエリを順に Q 個処理してください。
'1 x
':整数 x が与えられる。S に x が含まれているならば、S から x を取り除く。そうでないならば、S に x を追加する。'2 x
':S に含まれる整数 x が与えられる。S に含まれる数を頂点とし、差の絶対値が K 以下であるような数の間に辺を張ったグラフにおいて、x が属する連結成分の頂点数を出力する。
制約
- 1 \leq Q \leq 2\times 10^5
- 0 \leq K \leq 10^{18}
- 各クエリにおいて、1\leq x \leq 10^{18}
解法
数直線をイメージする。
クエリ1で数直線上の x の位置にコマを置いたり除いたりする。
K 以下の距離にあるコマ同士は同じグループとしたとき、クエリ2では、指定されたコマの属するグループサイズを答える。
そう考えると、各グループは数直線上で連続した区間のコマとなる。
順序を保ったまま、要素を挿入・削除・二分探索できる SortedSet があれば、状態を管理できる。
- \mathrm{ALL}:現在の全てのコマの位置の集合を管理するSortedSet
- \mathrm{LEFT}:現在の「各グループで最も左端のコマ」の位置の集合を管理するSortedSet
- \mathrm{RIGHT}:現在の「各グループで最も右端のコマ」の位置の集合を管理するSortedSet
クエリ1では、x を挿入(削除)することで両隣のコマ l,r とのグループ関係がどうなるかを、丁寧に場合分けして更新する。
(別々だったグループが統合される、左端だけ・右端だけ伸びる、x 単体の新たなグループができる、など)
更新には l,x,r の情報だけがあればよく、l よりさらに左のコマなどは知らなくても更新できるのがポイント。
クエリ2では、LEFT,RIGHT を二分探索することで x の属するグループの左端・右端がわかるので、 それぞれのALL上の位置を求めることで要素数が分かる。
Python は sortedcontainer がライブラリに入ったので、こういった問題がかなり解きやすくなった。
G - Freestyle
問題文
- 高橋くんは N 種類の泳ぎ方ができます。
- 高橋くんが i 種類目の泳ぎ方で泳ぐと、 1 秒あたり体力を A_i 消費して B_i [m] 進みます。
- Q 個のクエリに答えてください。そのうち i 個目は次の通りです。
- 消費する体力の合計を C_i 以下にして D_i [m] 進むことができるか判定し、進める場合は必要な最小の秒数を求めよ。
- ただし、高橋くんは泳ぎ方を自由に組み合わせることができ、泳ぎ方を変える体力と時間は無視できます。
- ※各泳ぎ方で泳ぐ時間は小数でもよい
制約
- 1 \le N \le 2 \times 10^5
- 1 \le A_i,B_i \le 10^9
- 1 \le Q \le 2 \times 10^5
- 1 \le C_i,D_i \le 10^9
解法
泳ぎ方の点 (A_i,B_i) をグラフにプロットして、意味を考えてみる。
「原点から、各泳ぎ方の点へのベクトル」が、1秒間、その泳ぎ方で進むことを表す。
2秒、3秒、泳いだらベクトルが2倍、3倍に伸びていくイメージ。
「ベクトルの傾き」が体力効率を示す。
各泳ぎ方の点の y 座標が速度(時間効率)を示す。
クエリで秒数を最小化する上ではなるべく速い(高い位置にある)泳ぎ方を使いたい。
泳ぎ方の点同士を結ぶ線分上の点は、「1秒間内でその2つの泳ぎ方を組み合わせる」ことを表す。
体力効率を上げる代わりに速度を落とす(または逆)みたいな選択ができる。
泳ぐ時間は小数でもよいので、線分上の任意の泳ぎ方ができる。
3つ以上の泳ぎ方を組み合わせるのは、「i,j を結ぶ線分上の点と、別の泳ぎ方の点 k の間」に
同様に線分を引くことを繰り返せば表現できる。
(結果的に、3つ以上を組み合わせた泳ぎ方は、下記の“意味のある泳ぎ方”になることはないが)
ある泳ぎ方に対し、体力効率も時間効率も劣る泳ぎ方は採用する意味が無いので、意味のある泳ぎ方は凸包みたいな形になる。
ただし、上側凸包でよく、さらに y 座標は狭義単調増加する範囲で良い。
まずこの部分的な凸包を構築する。
クエリが来たら、
- 最大体力効率より高い体力効率を求められるクエリは不可
- 意味のある泳ぎ方の中で最低体力効率(必然的に最速)以下の体力効率しか求められないクエリは、最速の泳ぎ方
その間にあるものは、凸包と、原点→クエリ点を結ぶ線の交点が、ちょうどよい体力効率の1秒当たりの泳ぎ方を示す。