Processing math: 100%

AtCoder Regular Contest 197 (Div. 2) A,B,C,D,E問題メモ

A - Union of Grid Paths

問題文

  • H×W のマス目があります.上から h 行目,左から w 列目のマスを (h,w) で表します.
  • さらに,'D', 'R', '?' からなる長さ H+W2 の文字列 S=S1SH+W2 が与えられます.
  • はじめ,すべてのマスは白色で塗られています.あなたは次の 3 つの手順からなる操作を何度でも行うことができます:
    1. 以下の条件をすべて満たす長さ H+W2 の文字列 X=X1XH+W2 をひとつ選ぶ.
      • XH1 個の 'D' および W1 個の 'R' からなる.
      • X は、S'?' を 'D' または 'R' に置き換えた文字列である。
    2. マス (1,1) に立つ.さらに i=1,2, の順に,文字 Xi の表す方向に 1 マス移動する.つまり,Xi'D' ならば下方向,Xi'R' ならば右方向に 1 マス移動する.なお,X が手順 1 の条件を満たすとき,移動先のマスは必ずマス目内に存在することが証明できる.
    3. 手順 2 においてあなたが通ったすべてのマス(最初・最後のマスも含む)について,そのマスが白色で塗られているならば,黒色で塗る.
  • 最終的に黒色で塗ることができるマスの個数としてありうる最大値を求めてください.
  • T 個のテストケースが与えられるので,それぞれについて解いてください.

制約

  • 1T2×105
  • 2H,W2×105
  • T,H,W は整数.
  • S'D', 'R', '?' からなる長さ H+W2 の文字列.
  • S に含まれる 'D' の個数は H1 以下.
  • S に含まれる 'R' の個数は W1 以下.
  • すべてのテストケースにわたる H+W の総和は 4×105 以下.

解法

S'?' を、先頭からなるべく 'D' を優先して置換したものを XD、'R' を優先して置換したものを XR とする。

答えは、XD の軌跡と XR の軌跡、およびそれに挟まれたマスとなる。

各行 i につき、以下を定義する。

  • firstD[i]:=XD の軌跡がはじめて i 行目に訪れた時の列index
  • lastD[i]:=XD の軌跡が i 行目から出る時の列index
  • XR に対しても同様に定義
  • XD は最大限早く下に降りる軌跡なので、(i,firstD[i]) より左側はどんな X でも通ることはできない。
  • XR は最大限長く上の方の行に留まる軌跡なので、(i,lastR[i]) より右側はどんな X でも通ることはできない。
  • その間は適当に置換した 'D' と 'R' を入れ替えれば通れる。

よって、Hi=1lastR[i]firstD[i]+1 が答えとなる。

Python3

B - Greater Than Average

問題文

  • 空でない整数列 x=(x1,,xn) のスコアを,「x の要素のうちで x の平均値より大きなものの個数」として定めます.
    • つまり,x のスコアは xi>x1++xnn を満たす i の個数です.
  • 長さ N の整数列 A=(A1,,AN) が与えられます.
  • A の空でない部分列に対するスコアの最大値を求めてください.
  • T 個のテストケースが与えられるので,それぞれについて解いてください.

制約

  • 1T2×105
  • 2N2×105
  • 1Ai109
  • 入力される値はすべて整数
  • すべてのテストケースにわたる N の総和は 2×105 以下.

解法

とりあえず順番は関係ないのでソートする。

例えば、最適解が以下のような形だったとすると、

  小←---------------→大
A:  o  o  o  o  o  o  o     ~: 最適解で x に採用した値
    ~       |~  ~     ~     |: 最適解の x の平均値

平均以下の値を追加して平均値が上がることはないので、採用していない平均以下の値は採用していい。
これによってスコアが悪化することはない。

  小←---------------→大
A:  o  o  o  o  o  o  o
    ~  ^  ^| ~  ~     ~

また、平均より大きい2要素 i<j で、i を採用せず j を採用している場合、 代わりに i を採用することにして平均が上がることはない。

  小←---------------→大
A:  o  o  o  o  o  o  o
    ~  ~  ~| ~  ~  i  j

これを繰り返していくと、結局、スコアを悪化させず 「小さい方から k 個を全て採用」という形にできる。

他の最適解があっても、必ず同じスコアで 小さい方から全て採用した形も存在すると言えるので、そういう形のみ調べればよい。

これは、k=1,2,3,...,N の順に素直に平均を計算し、 A から平均を超える位置を二分探索することで、O(NlogN) などで全て調べられる。

Python3

C - Removal of Multiples

問題文

  • すべての正整数からなる集合 S があります.
  • Q 個のクエリを順に処理してください.i 番目のクエリでは,2 以上の整数 Ai および,正整数 Bi が与えられるので,次の 2 つのことを順に行ってください.
    • S から Ai の倍数である要素をすべて削除する.
    • S の要素のうち小さい方から Bi 番目のものを出力する.
      • なお本問の制約のもとで,S はこの時点で Bi 個以上の要素を含むことが証明できる.

制約

  • 1Q105
  • 2Ai109
  • 1Bi105
  • 入力される値はすべて整数

解法

上限見積もりと計算量見積もりをちゃんとできますか、という問題。
ただ、できなくても本番中では勘やダメ元で解けた人もいそう。(手段がそれくらいしか無さそう、というのもある)

削除するパートはひとまず置いといて、答えを求めるパートは「x 以下で残っている S の要素数はいくつ?」が解ければ x を二分探索できる。 だが、それにも二分探索のスタートとなる「答えの上限」が必要となる。

答えの上限を見積もる。
素数に着目する。素数は、Ai によって直接的に指定されない限り、S から削除されることは無い。
よって、Q105 個の素数が削除された後、それ以外で小さい方から Bi105 番目の素数より大きな値が答えとなることは無い。
つまり、答えは 2×105 個目の素数以下である。
そしてこれは、調べると 2.8×106 程度であることが分かる。(これを L とする)

1L くらいならあらかじめ全て S として持っておくことができるので、 FenwickTree や SegmentTree で実装すれば、「x 以下の個数」がわかるため二分探索は可能となる。

残る問題は「削除パートで、SegTree から Ai の倍数を削除する方法」である。
愚直にやってたら、毎回 L/Ai の計算量がかかり、TLEしそう。上手い方法があるのか……?

実は愚直で良い。「過去にクエリとして飛んできた Ai と同じものは再処理しない」などの自明な枝刈りをすれば通る。

Ai が全て異なる場合、調べる箇所数は以下となる。

  • LA1+LA2+...+LAQ

これは、Li=1Li より小さい。
そしてこの式のオーダーは O(LlogL) となることが知られている。

Python3

D - Ancestor Relation

問題文

  • N×N 行列 A=(Ai,j) (1i,jN) が与えられます.A の成分は 0 または 1 です.
  • 頂点に 1 から N までの番号が付けられた N 頂点の木 G であって,次の条件を満たすものの個数を 998244353 で割った余りを求めてください.
    • Ai,j=1 であることと,以下の 2 条件のうち少なくともひとつが成り立つことは同値である:
      • 頂点 1 を根としたとき頂点 j は頂点 i の祖先である.つまり頂点 j は,頂点 1,i を結ぶ G 上のパス上にある.
      • 頂点 1 を根としたとき頂点 i は頂点 j の祖先である.つまり頂点 i は,頂点 1,j を結ぶ G 上のパス上にある.
  • ただし,パスの端点もパス上にあると見なします.G は木であるとき,2 頂点を結ぶ G 上のパスは一意に存在することにも注意してください.
  • T 個のテストケースが与えられるので,それぞれについて解いてください.

制約

  • 1T105
  • 2N400
  • Ai,j{0,1} (1i,jN)
  • Ai,i=1 (1iN)
  • Ai,j=Aj,i (1i,jN)
  • すべてのテストケースにわたる N2 の総和は 4002 以下.

解法

行列 Ai 行目を、長さ N のbitフラグとして捉えたものを Bi とする。

頂点 i に着目したとき、Ai,j=1 は、「i の祖先か子孫に j がいる」ことと同じになる。

❶--●--●-...-(i)--●--●--●    iにとって、
 |       |        `-●--●        ●: A_{i,j}=1 である相手頂点
 `--○   `--○--○   `--●        ○: A_{i,j}=0 である相手頂点
             `--○

i とその子孫 j にとって、BiBj となる。

❶--●--●-...-(i)--◎--◎--◎    ●: i,j ともに 1 である相手
 |       |        `-●--(j)       ◎: i のみ 1 である相手
 `--○   `--○--○   `--◎        ○: i,j ともに 0 である相手

このような包含関係が成り立つように、各 Bi に矛盾せず木を作れるか?をまず判定する必要がある。

ところで、サンプルなどからも確認できるように、ij の間に枝分かれが無く1本道の場合、Bi=Bj となる。

①--②--③--④    ②③④同士、⑤⑥同士は、それぞれBiが同じとなる
 `--⑤--⑥--⑦
         `--⑧

よって、Bi が同じ頂点同士は縮約して考える。

  • D=(D1,...,Dk):=B から重複を除いたもの
  • G=(G1,...,Gk):=(Bj=Di) である j の集合をbitフラグ化したもの
  • C=(C1,...,Ck):=(Bj=Di) である j の個数

これを元に、矛盾の判定方法を考える。

まず、頂点1は全ての頂点と子孫関係にあるので、B1=1111...1 でなければならない。0が含まれる場合は矛盾なので 0 通り。

D をbit中に含まれる '1' が多い順にソートする。(G,C もそれに合わせて並び替える)
これで、i の親 p は(存在するとすれば)p<i の範囲にあることが保証され、考えやすくなる。

i=2,...,k の順に、以下を調べる。

  • j=1,...,i1 に対し、DiDj か(Di&Dj=Di か)調べる。
    • 当てはまる場合、ji の祖先である。
      • GjDi か調べる。違ったら矛盾。
    • 当てはまらない場合、ji はどこかで分岐した別の枝にあり、子孫関係にあってはいけない。
      • DiGj に共通部分が無いか調べる。存在したら矛盾。
      • DjGi に共通部分が無いか調べる。存在したら矛盾。

最後まで矛盾が無ければ、各 Di は「自分と祖先・子孫関係にある頂点番号は立ち、 それ以外の頂点番号は立っていない」ので、問題文中の定義通りであることが確認できる。

O(N3) で判定できる。

矛盾が無い場合、条件を満たす木から、Bi が同じもの同士を入れ替えても成り立つ。
逆に言うと、D から導かれる親子関係はガチガチに決まってしまうため、それくらいしか自由度が無い。
i につき並べ方は Ci! 通りだが、C1 に限っては、必ず根で無ければならない頂点1を含む。

答えは、(C11)!×ki=2Ci! となる。

Python3

E - Four Square Tiles

問題文

  • 正整数 N,H,W が与えられます.ただし,H,W3N1 が成り立ちます.
  • H×W のマス目に N×N の正方形のタイルを 4 個置く方法であって,以下の条件をすべて満たすものの個数を 998244353 で割った余りを求めてください.
    • 各タイルは,マス目の ちょうど N2 個のマスを完全に覆う.
    • ひとつのマスが複数のタイルによって覆われてはならない.
  • ただし,タイル同士は区別しません.
  • T 個のテストケースが与えられるので,それぞれについて解いてください.

制約

  • 1T2×105
  • 1N,H,W109
  • H,W3N1
  • 入力される値はすべて整数

解法

H,W3N1 より、タイルは3つは縦横に並べられないという制約がある。

タイルは、位置関係を「左上、右上、左下、右下」と呼べるような位置関係にしか配置できない。

グリッドを2次元座標と捉え、格子点を (0,0)(H,W)(左上が原点)とし、各タイルの位置を以下のように定義する。

  • (y1,x1): 左上タイル(タイル1とする)の右下の格子点座標
  • (y2,x2): 右上タイル(タイル2とする)の左下の格子点座標
  • (y3,x3): 左下タイル(タイル3とする)の右上の格子点座標
  • (y4,x4): 右下タイル(タイル4とする)の左上の格子点座標
(y1,x1) (y2,x2)
 ─┛     ┗─
 ─┓     ┏─
(y3,x3) (y4,x4)

こうすると、全て共通して NyiHN,NxiWN という制約にできる。
また、重ならない判定も、xixj or yiyj のように、単純な形になる。

この上で配置を数えたいのだが、取っかかりとして、以下を使う。

  • y1y4 または y2y3 の少なくともいずれか一方は成り立つ(★)

要は、対角上にあるタイルの組のどっちかは、高い方の下辺が、低い方の上辺と、同じか、より上にある。
こうなっているペアをまず固定して、そこに残り2つを配置することを考えると、 タイルが重なるような配置が限定され、多少考えやすくなる(ような気がした)。

  • (★)が成り立つペアを(1,4)に固定した場合の数 × 2 - 両方のペアが(★)を満たす場合の数

が、答えとなる。

「(★)が成り立つペアを(1,4)に固定した場合の数」を考える。y 座標と x 座標を別々に考える。
まず、「タイル (2,3) が重なってもよい場合の数」を数え、重なる場合の数を引く。

  • 重なっても良い場合の数
    • y 座標
      • 以下のいずれかの大小関係を満たす (y1,y2,y3,y4) の組の個数
        • Ny1y2y3y4HN
        • Ny2y1y3y4HN
        • Ny1y2y4y3HN
        • Ny2y1y4y3HN
        • Ny1y3y2y4HN
      • それぞれは (H2N+44) だが、単純に5倍ではどこかで=が成立する場合に重複する。
      • 包除原理で「=が成立する個数」で場合分けし、重複を除く。
      • 5(H2N+44)5(H2N+33)+(H2N+22)
    • x 座標
      • Nx1x2WN となるような (x1,x2) の数」の2乗
      • (W2N+22)2
  • 重なる場合の数
    • 以下の2つが同時に成り立つとき、重なる。
      • Ny1y3<y2y4HN
      • Nx1x3<x2x4WN
    • 差分を取って考えると、H2N1 個のボールを5個の箱に重複を許して入れると考えられる
    • (H2N+34)×(W2N+34)

「両方のペアが(★)を満たす場合の数」を考える。
x 座標は重なっても良い場合と変わらない。
y 座標は、重なっても良い場合から最後の Ny1y3y2y4HN を除いたものとなる。

  • 同様に包除原理で考えて、
    • 4(H2N+44)4(H2N+33)+(H2N+22)

以上で必要な情報は揃った。いずれも O(1) で計算できるため、全体 O(T) で求められる。

Python3

より簡単な解法

上の解法で、途中で「yx を分けて考えていい」と気付いて、 フラットに最初からもう一度考えると確かにこの解法はわかるんだけど、 試行錯誤の途中だとなかなか今試している解法から脱却できないね。

2変数ずつ4ペアを、独立に決めればよい。

    N ≦ x1 ≦ x2 ≦ W-N
         │    │
N        │    │       N
∧       │    │       ∧    ※∧は≦の縦書きの代用のため
y1 ───┘    └─── y2      実際はイコールを含む
∧                      ∧
y3 ───┐    ┌─── y4
∧       │    │       ∧
H-N      │    │       H-N
         │    │
    N ≦ x3 ≦ x4 ≦ W-N

この決め方は、(H2N+22)2(W2N+22)2 となる。

このように座標を決めると、上下左右に隣り合うタイルは重ならないことが保証されるが、 「対角上にある位置関係のタイル」のみ、重なっているものまで数えている。

斜めのタイルが重なる場合の数は1つめの解法で求めたように、(H2N+34)(W2N+34) となる。
重なりうるペアは2通り(1と4、2と3)あり、この2ペアが同時に重なることはないため、単純に全体からこの2倍を除けばよい。

Python3

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