Loading [MathJax]/extensions/TeX/mathchoice.js

第六回日本最強プログラマー学生選手権-予選-(AtCoder Regular Contest 201)A,B,C,D,E問題メモ

A - CatCoder Double Contest

問題文

  • 2222 年の CatCoder では、CatCoder Double Contest(以下、C2C と略します)を開催することになりました。
  • いま、問題案を持っている writer が N 人います。
  • 各 writer の問題案は難易度によって Hard, Medium, Easy の 3 種類に分類されており、i 人目の writer が持っている Hard, Medium, Easy の問題案の個数はそれぞれ Ai,Bi,Ci です。
  • 各 C2C では Div.1, Div.2 の 2 部門を同時に 1 つずつ開催します。それぞれの部門の開催に必要な問題案は以下の通りです。
    • Div.1:同じ writer の Hard, Medium の問題案を 1 つずつ
    • Div.2:同じ writer の Medium, Easy の問題案を 1 つずつ
  • ここで、Div.1, Div.2 の writer は必ずしも同じである必要がない点に注意して下さい。また、各問題案は高々 1 回の C2C の 1 つの部門にしか使用出来ません。
  • C2C を最大で何回開催出来るかを求めて下さい。
  • T 個のテストケースが与えられるので、それぞれについて答えを求めて下さい。

制約

  • 1T105
  • 1N2×105
  • 1Ai,Bi,Ci109
  • 全てのテストケースにおける N の総和は 2×105 以下
  • 入力される値は全て整数

解法

B 個あるMediumがどちらのDivでも使えるので、どっちで使うかが問題となる。

各writerの問題を、「Div1確定セット」「Div2確定セット」「どっちか一方のみに使えるセット」に分ける。

  • d1 Div1確定: min(Ai,BiCi)
    • A と、「C と最大限組にした上で残っている B」の小さい方。
  • d2 Div2確定: min(Ci,BiAi)
    • C と、「A と最大限組にした上で残っている B」の小さい方。
  • d3 どちらか: min(Ai+Ci,Bi)d1d2

全writerで、d1,d2,d3 をそれぞれ合計したものを D1,D2,D3 とし、 D3 をいい具合にDiv1,Div2に分配すればよい。

Python3

B - Binary Knapsack

問題文

  • 1,2,,N の番号がついた N 個の品物があります。
  • 品物 i の重みは 2Xi で価値は Yi です。
  • 重みの和が W 以下になるように品物を 0 個以上選ぶとき、価値の和としてありうる最大値を求めて下さい。
  • T 個のテストケースが与えられるので、それぞれについて答えを求めて下さい。

制約

  • 1T105
  • 1N2×105
  • 1W1018
  • 0Xi<60
  • 1Yi109
  • 全てのテストケースにおける N の総和は 2×105 以下
  • 入力される値は全て整数

解法

ナップサックに見えて貪欲解法。

全ての重みについて、価値 0 の品物が無限にあるとし、重みの和をちょうど W にすると考える。

たとえば W=13(2進数で1101)の場合、Xi=0(重み 1)の品物は絶対に1つ使う。
Xi1 の品物をどう組み合わせても、絶対に 1 の端数は残るため。
よって、Xi=0 の中で価値最大の1つをまずは確定させてよい。

残りの Xi=0 の品物は、2個1組として Xi=1 が1つと考えてよい。
この時も、より上の桁で使うなら価値の高い方から使うので、価値の高い方からペアにしていく。
1個余る品物があったら、最初に仮定した価値0の品物と組み合わせると考える。

つまり、以下を x=0,1,2,... と小さい方から繰り返せばよい。

  • Xi=x の品物を価値の高い順にソートする
  • Wx bit目が立っている場合
    • 1番目に価値が高いものを採用確定し、(2,3),(4,5),... 番目に価値が高いものをペアにして Xi=x+1 の品物1つとする
  • Wx bit目が立っていない場合
    • (1,2),(3,4),... 番目に価値が高いものをペアにして Xi=x+1 の品物1つとする

計算量は、Xi=0 の品物が大量にあり多くの品物が何回も繰り上がるときが最も大きくなるが、 その場合でも N+N2+N4+... なので全体 O(N) の処理である。
x でのソートが発生するため、O(NlogN+D) となる。

Python3

C - Prefix Covering

問題文

  • どの文字も 'A' または 'B' であるような空でない文字列を AB 文字列と呼ぶことにします。
  • AB 文字列からなる集合 X が次を満たすとき、良い集合であるということにします。
    • 長さ 10100 であるような AB 文字列は、必ずある X の要素を接頭辞に持つ。
  • 相異なる AB 文字列 S1,S2,,SN が与えられます。
  • k=1,2,,N について、集合 {S1,S2,,Sk} の部分集合であって良い集合であるものの個数を 998244353 で割った余りを求めて下さい。

制約

  • 1N2×105
  • S1,S2,,SN は相異なる AB 文字列
  • 1iN|Si|5×105

解法

S={S1,S2,...,Sk} とする。

問題文の意味がちょっと取りづらいが、要は、以下のようなTrieがあったとして、

        ○
    A/    \B       ●: S に存在する文字列
   ○        ●
 A/\B    A/\B
 ●  ○    ○  ○
A/\BA/\B  A/\BA/\B
○○●○  ○●○○

「『●に当たらずに下まで抜けられる』ようなルートが存在しないような●の集合」が、良い集合となる。
例えば上の例は、ABB… が下まで抜けられてしまうため、S は良い集合ではない。

Trieとセグメント木を混ぜ合わせたようなデータ構造で解くことを考える。

各ノードが以下の情報を持っていれば、セグメント木の要領で、2つの子の情報から自身の情報を計算できる。

  • fv: 自身が●かどうかフラグ
  • cv: 自身の部分木以下にある●の個数
  • bv: 自身の部分木以下にある●の選び方で、ルートを封鎖するようなものの個数

fv,cv は簡単なので、bv の計算方法を考える。

自身が●でない(fv=false)場合、左右の子を l,r として、単純に以下のようになる。

  • bv=bl×br

自身が●(fv=true)の場合、「自身を部分集合に加えない」場合の選び方は上記と一致し、 加えて「自身を部分集合に加える」場合は、それより下はどう選んでもいいので、

  • bv+=2cl+cr

となる。

これで、各 Si の追加時に O(|Si|) で情報を更新し、その時々の broot が答えとなる。

静的なセグメント木で作るとノード数が多くなりすぎるため、動的セグメント木で実装すると、計算量は O(|Si|) となる。

Python3

D - Match, Mod, Minimize

問題文

  • 長さ N の非負整数列 A=(A1,A2,,AN),B=(B1,B2,,BN) と正整数 M が与えられます。
  • A の要素を自由に並び替えることが出来るとき、max1iN((Ai+Bi)modM) としてありうる最小値を求めて下さい。
  • T 個のテストケースが与えられるので、それぞれについて答えを求めて下さい。

制約

  • 1T105
  • 1N3×105
  • 1M109
  • 0Ai,Bi<M
  • 全てのテストケースにおける N の総和は 3×105 以下
  • 入力される値は全て整数

解法

単純そうだが、上手い言い換えを挟まないとなかなか O(N2) から減らない。

もし、MODが無ければ答えは簡単で、A の昇順と B の降順を対応づけたものが最善のペアとなる。
(そうでないペアの作り方があったとして、上記の対応に組み替えて損しないことが示せる)

modを考慮する場合、制約から、Ai+Bi は、0 以上 2M 未満となる。
(Ai+Bi)modM は、0Ai+Bi<M の時 Ai+BiMAi+Bi<2M の時 Ai+BiM となる。

和が M 以上 2M 未満になることを、「繰り上がる」と表現することにする。

ある最適解を実現するペア分けがあったとして、各ペアは「繰り上がるペア」と「繰り上がらないペア」に分けられる。
このうち、「繰り上がらないペア」は、modを考慮しない場合と同じく、損しない組み替えを重ねることで 繰り上がらないペアの中で A の昇順と B の降順を対応づけるように組み替えることができる。

   繰り上がらないペア
A  ○  ○   ○  ○
     \_ \ / _/
         -X-
     /~ / \ ~\
B  ○  ○   ○  ○

繰り上がるペアでも似たような組み替えは成り立ちそうなのだが、 組み替えた結果、繰り上がらないペアに変化するパターンがあり、 必ずしも「損しない」組み替えとなる保証がない。
このあたりの証明を詰め切れなかった。


B'_i = (-B_i) \mod{M} とした上で、 A_i+B_iA_i-B'_i と言い換えると少し見通しが良くなる。

この場合、A_i \lt B'_i ならmodの結果が M+A_i-B'_i となるので、 あらかじめいくつかの A_iM を足しておき、全て A_i \ge B'_i となるような組を作ることを考える。

M を足した後では、A,B' の昇順にマッチングするのが明らかに最適である。
問題は、どのように M を足す数と要素を選ぶべきか。

結論から言うと、条件を満たすまで A の中の最小の数に M を足す、ということを繰り返して問題ない。

略証

よって、A の小さい方から何個に M を足せば、全て A_i \ge B'_i となるかがわかればよい。

  • A の末尾に、A_1+M, A_2+M,...,A_N+M をこの順に加えたものを A' とする。
  • B_i から、自身以上となる A'_j の位置を二分探索で探す。
  • d_i=\max(0,j-i) が、その i にとって、少なくとも M を足さなければならない個数の最小値となる。
  • \max_i d_i が、最適な M を足す個数となる。

最適なシフト数が分かれば、実際に試して答えが求められる。

計算量は O(N \log{N}) となる。

Python3

E - Total Area of Bounding Boxes

問題文

  • 2 次元平面上に 1,2, \dots, N の番号がついた N 個の点があります。
  • i の座標は (i,Y_i) です。
    • なお、この問題では Y=(Y_1,Y_2,\dots ,Y_N)(1,2, \dots ,N) の順列であることが保証されます。
  • \lbrace \texttt{点} 1,\texttt{点} 2,\dots ,\texttt{点} N \rbrace の要素数 2 以上の部分集合 S 全てに対するバウンディングボックスの面積の総和を 998244353 で割った余りを求めて下さい。
  • S に対するバウンディングボックスとは、 S に含まれる全ての点を内部または周上に含んでかつ x 軸に平行な辺を持つような長方形のうち、面積が最小であるものを指します。

制約

  • 2 \le N \le 2 \times 10^5
  • Y(1,2, \dots ,N) の順列
  • 入力される値は全て整数

解法

主客転倒で、「ある 1 \times 1 のマスがバウンディングボックスに含まれるような点の部分集合の個数」を考える。

  
   A  ┃  ┃  B
━┿━┛─┗━┿━
  │  │■│  │
━┿━┓─┏━┿━
   C  ┃  ┃  D

あるマスの、左上、右上、左下、右下の領域を、それぞれ A,B,C,D とする。
また、それぞれに含まれる点の個数を、a,b,c,d とする。(太線の境界上の点も含む)

包除原理で考える。

全ての点の選び方 2^N

AB だけから選ばれた場合」「AC だけから選ばれた場合」 「BD だけから選ばれた場合」「CD だけから選ばれた場合」は ■はバウンディングボックスに含まれない。

上記のうち、「A だけから選ばれた場合」「B」「C」「D」は重複して引かれている。

上記のうち、「全てが選ばれない」状態は重複して加算されている。

以上をまとめると、以下が、■がバウンディングボックスに含まれる部分集合の個数である。

  • 2^N - 2^{a+b} - 2^{a+c} - 2^{b+d} - 2^{c+d} + 2^a + 2^b + 2^c + 2^d - 1

(N-1) \times (N-1) の全ての■についてこれを計算すれば答えとなる。

これ、1つ1つは加減算なので、かなり大胆にバラして考えられる。

  • 2^N-1 は、どの■でも加算される
  • 2^{a+b} など、指数が2数の足し算となっている部分は、
    • 例えば上から5行目の N-1 個のマスはどのマスでも 2^{a+b}=2^5 となる。
    • 4方向それぞれ、同じ考え方ができるので4倍する。
    • (2^1+2^2+...+2^{N-1}) \times (N-1) \times 4

ということで、厄介なのは 2^a,2^b,2^c,2^d の、指数が単独文字の項となる。

ひとまず、全ての■についての 2^a の合計を求めることを考える。b,c,d も同様に考えられる。

   1  2  3  4  5    ←格子点の座標
    1 2 3 4     ←マスのindex
1     '
2  '
3           '
4              '
5        '

  ↓
    1 2 3 4
1    0' 1  1  1    各マスの a の値
2  ' 1  2  2  2    (N行目は存在しない点に注意)
3    1  2  2' 3
4    1  2  2  3'
5        '

これの、各 2^a の総和を求めたい。

セグメント木で求められる。

  • 載せるもの: (自身の管理する区間内の点の個数, 自身の管理する区間だけにおける各マスの 2^a の総和)

ちょっと2つめが言葉で説明しにくいが、

a: 1 2 2 3  3 4 5 5
            ~~~~~~~    ←この区間を表すノードの場合、
            0 1 2 2    ←区間外の点を考慮しないなら a はこうなってるはずで、
            1+2+4+4=11 ←その2^aの総和である11を持たせる。
a: 1 2 2 3  3 4 5 5
   ~~~~~~~~~~~~~~~~    ←左のノードとマージするとき、
                         左ノードにある点の個数が2個だとして、
           4+8+16+16=44  右ノードの値は一律 2^2 倍となる。
                         (後は左ノードの値をそのまま加算すればマージ完了)

i=1,2,... の順に (i,y_i) に点を打った状態に更新し、 その都度、その列全体の 2^a の総和(要は、ルートノードの2つめの値)を加算していけばよい。

    1 2 3 4
1    0' 1  1  1    各マスの a の値
2  ' 1  2  2  2
3    1  2  2' 3
4    1  2  2  3'
     v  v  v  v
     7 14 14 22  →  全体の 2^a の総和は 57

2^a2^c2^b2^d はそれぞれ、 上からの累積和と下からの累積和でやることが似ているので まとめてやると、i=1,2,…,N の順と i=N,N-1,...,1 の順の2回の走査で全て求められる。

これで、必要な情報が全て計算できた。

Python3

programming_algorithm/contest_history/atcoder/2025/0622_arc201.txt · 最終更新: by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0