−目次
AtCoder Beginner Contest 405(Promotion of AtCoder Career Design DAY)E,F,G問題メモ
E - Fruit Lineup
問題文
- A 個のリンゴと B 個のオレンジと C 個のバナナと D 個のブドウがあります。
- これらの A+B+C+D 個の果物を、以下の条件全てを満たすように左右一列に並べる方法は何通りありますか?
- リンゴはすべて、バナナよりも左側に並べる。
- リンゴはすべて、ブドウよりも左側に並べる。
- オレンジはすべて、ブドウよりも左側に並べる。
- ただし、同じ種類の果物同士は区別できないとします。
- 答えを 998244353 で割った余りを求めてください。
制約
- 1≤A≤106
- 1≤B≤106
- 1≤C≤106
- 1≤D≤106
- A,B,C,D は全て整数
解法
まず、A 個のリンゴと D 個のブドウをこの順に並べる。
リ リ リ リ リ リ ブ ブ ブ ブ ブ ブ
B 個のオレンジはここに挿入できて、
リ リ リ リ リ リ ブ ブ ブ ブ ブ ブ ^ ^ ^ ^ ^ ^ ^
C 個のバナナはここに挿入できる。リとブの間に挿入したオの数により、バの挿入箇所の数が異なる。
リ リ リ リ リ リ オ オ ブ ブ ブ ブ ブ ブ ^ ^ ^ ^ ^ ^ ^ ^ ^
よって、リとブの間に挿入するオの数を i=0~B に固定した場合の数をそれぞれ求めればよい。
- B∑i=0AHB−i×D+i+1HC
F - Chord Crossing
問題文
- 円周上に 2N 個の点が等間隔に並んでおり、ある点から始めて時計回りに 1,2,…,2N の番号が付けられています。
- これらの点を結ぶ M 本の線分 1,2,…,M が存在し、線分 i は点 Ai と点 Bi を結んでいます。
- ここで、Ai と Bi は相異なる 偶数 です。
- また、これらの線分のうちのどの 2 つの線分も共有点を持たないことが保証されます。
- Q 個のクエリを処理してください。j 番目のクエリは以下の通りです。
- 相異なる 2 つの 奇数 Cj,Dj が与えられる。
- 上で与えられた M 本の線分 1,2,…,M のうち、点 Cj と点 Dj を結ぶ線分と共有点を持つものの本数を求めよ。
制約
- 2≤N≤106
- 1≤M≤min(⌊N2⌋,2×105)
- 1≤Q≤2×105
- 1≤Ai<Bi≤2N
- 1≤Cj<Dj≤2N
- Ai,Bi は偶数
- Cj,Dj は奇数
- どの i1,i2 (i1≠i2) についても、線分 i1 と線分 i2 は共有点を持たない
- 入力は全て整数
解法
1 と 2N で輪をぶった切り、直線に開く。線分とクエリを区間で表現する。
1 2 3 4 5 6 7 8 線分 |---| |---| クエリ |---| → 1 |-------| → 0 |-------| → 2
クエリの答えは、クエリ区間に「左端と右端の内いずれか一方だけを含む」線分区間の個数となる。
片方ずつ別々に求める。まずは、各線分区間に対し「左端を含み右端は含まない」クエリ区間の個数を求める。
これは、長さ 2N のセグメント木 S を用意し、 イベントソートのように i=1,...,2N の順に以下を処理すれば可能となる。
- i を左端とする線分区間に対し、S[i] に1加算する
- i を右端とする線分区間に対し、その左端を j とし、S[j] から1引く
- i を右端とするクエリ区間に対し、S 上の Ci~Di の和を答えとして加算する
同様のことを逆順にやると、「右端を含み左端は含まない」クエリ区間も求められる。
G - Range Shuffle Query
問題文
- 長さ N の数列 A=(A1,A2,…,AN) が与えられます。
- Q 個のクエリが与えられるので処理してください。
- クエリでは整数 L,R,X が与えられるので次の問題を解いてください。
- A の L 番目から R 番目までの要素を取り出してできる数列 B=(AL,AL+1,…,AR) があります。
- あなたは以下の一連の操作をちょうど 1 回行います。
- まず、B から値が X 以上の要素を全て取り除く。
- 次に、B の要素を自由に並べ替える。
- 操作後の B としてあり得るものは何通りありますか?答えを 998244353 で割った余りを求めてください。
制約
- 1≤N≤2.5×105
- 1≤Q≤2.5×105
- 1≤Ai≤N
- 1≤L≤R≤N
- 1≤X≤N
- 入力される値は全て整数
解法
クエリの答えは以下となる。クエリ [L,R] 間にある値 a の個数を Ca とする。
- (クエリ区間内にあるX未満の値の個数)!×X−1∏a=11Ca!
よって、各クエリ区間 [L,R] における
- X 未満の値の個数
- X 未満の値の 1Ca! の総積
がわかればよいということになる。これは、例えば以下のデータ構造やテクニックで管理できる。
- Fenwick Tree, Segment Tree
- Ai を添字に持ち、個数の区間SUMと、階乗の逆数の区間PRODを2本で管理
- 平方分割
- 同様に Ai を添字に持ち、個数の区間SUMと、階乗の逆数の区間PRODを管理
Mo's Algorithm で、区間を伸び縮みさせ Ai が入ったり出たりしたときに、これらのデータ構造の状態を適切に更新できればよい。
ここで、一般的には FenwickTree と平方分割では前者で実装する方が速そうだが。。。
今回の場合、前者ではTLE、後者が想定解法となる。なんてこった。
Mo's Algorithm では、以下の2つの処理が必要となる。
- (A) 区間を伸び縮みさせたとき、状態を更新する処理
- (B) 伸び縮み後、答えを集計する処理
Mo's Algorithm で回数が多いのは (A) の方である。
そしてこの問題の場合、
- FenwickTree,SegmentTree:
- (A) の計算量は O(logN)、(B) の計算量は O(logN)
- 平方分割:
- (A) の計算量は O(1)、(B) の計算量は O(√N)
回数の多い処理(A)において平方分割の方が優れた計算量となる。
全体の計算量は、Mo's のバケットサイズを N√Q、平方分割のバケットサイズを √N とすることで、O(N√Q+Q√N) となる。 第1項が(A)、第2項が(B)の計算量となる。 ちなみに、FenwickTree解法では O(N√QlogN+QlogN) で、バランスが悪くなっていることが分かる。
AtCoderでは遅い言語でもACできるよう、想定解法にlog1個つけてもACできるくらい余裕のある実行時間制限の問題が多いように思うが、今回はlogを落とすことが要点となった。