hitoare日記

たまに書きます

F - Range Set Query(AtCoder Beginner Contest 174)

問題リンク

atcoder.jp


問題概要

\(N\)要素の数列\(c_1,...,c_N\)が与えられる。

以下の\(Q\)個のクエリを処理せよ。

  • クエリ\(i\):\(c_{l_i}\)から\(c_{r_i}\)までの要素の種類数を答える。
解法

 要素数は\(r_i - l_i + 1\)なので、これから同じ要素が重複している分だけ引くことを考える。

\(c_j=c_k\)かつ\(j\)と\(k\)の間に同じ値の要素が存在しないような全てのペアに対し、

区間\([l_i,r_i]\)が\([j,k]\)を完全に含んでいるようなペアの個数を引く、という方針で考える。

上のような\( (j,k) \)の組み合わせは\(O(N)\)個あるので、効率的に処理する必要がある。

また、「区間を完全に含んでいる」は「\(l_i \leq j \)かつ\(k \leq r_i\)」という2つの条件が絡んでいるので面倒である。

こういう時は、片方の条件が常に成り立つようにソートをして点を追加しつつクエリに答えることで上手くいくことがある。今回の場合、

  • 全ての区間(\([l_i,r_i]\)と\([j,k]\)たち)を左端に関して降順にソート。
  • ただし等しい場合は\([j,k]\)が先にくるようにする。
  • \([l_i,r_i]\)が来た時は、それまでに登場した\(c_k\)のうち\(r_i\)以下であるような物の個数が含まれる区間の個数なので、i番目のクエリの答えは\( (r_i - l_i + 1) \)からその個数を引いたものになる。

左端の降順に並べたことで、それまでに追加された点に対し\(l_i \leq j\)が常に成り立っていることが肝心である。

後は、点を追加しつつ、ある値以下の点の個数を答えればよいので、これはBITを使えば求められる。

提出

https://atcoder.jp/contests/abc174/submissions/15624952

感想

最初累積和の変形で考えたのでかなりタイムロスした。

上位陣がめちゃくちゃ速いし典型っぽい。

(追記:Mo's algrorithmというのがあるそうです)

E - M's Solution(M-SOLUTIONS プロコンオープン 2020)

解説と異なる解法でした。

問題リンク 

atcoder.jp


問題概要

平面上にN個の集落があり、各集落iは座標\( (X_i,Y_i) \)にあって重み\(P_i\)が与えられている。

また、X軸とY軸には線路がある。

この時、X軸またはY軸に平行な線路をK本追加して、「重み×最も近い線路までの距離」の和Sを最小化したい。

最小値を\(K=0...N\)まで求めよ。

解法

線路は集落を通るように敷くのが最適なので、候補は実質2N本しかない。

片方の向きの敷き方を固定して、もう片方の向きの線路の敷き方はDPする。

集落はX座標の昇順にソートしておく。

 

X軸に平行な線路( \(y=Y_i\) )の敷き方を1つ決め打ちし、最初に敷いてしまう。

この時点での距離の最小値を各集落に対しまず求めておく。

次にY軸に平行な線路( \(x=X_i \) )の敷き方に関してDPを行う。

\(dp[i][j][k]\) = (i番目の集落まで見て、最後に敷いた線路が集落jを通り、既に線路をk本敷いている時の集落jまでのSの最小値)

とする。(ただしj=0は線路を敷いていないとする)

この時\(j < i\)ならば

\(dp[i][j][k] = dp[i-1][j][k]\)

である。

また新たに集落iを通る線路を敷く事を考えると

\(dp[i][i][k+1] = min \{0 \leq j \leq i-1 | dp[i-1][j][k] + \sum_{l = j+1}^{i-1} P_l \times (集落lの距離) \} \)

となり、集落lの距離は前計算した物と集落j,iを通るものだけを考えれば良い。

 

\( dp[N][j][k] + \sum_{l = j+1}^{N} P_l \times (集落lの距離) \)

の最小値がこの場合での本数kでのSの最小値になる。

 

これを\(y=Y_i\)の敷き方\(2^N\)通り全てに対し試せばよい。

計算量は\(O(2^N N^4)\)になる。

定数は軽いが906msだったので結構危ない。

(7/26 追記:Σの部分を使いまわしたら普通にループが一個外せた。\(O(2^N N^3)\) で624ms。)

提出

https://atcoder.jp/contests/m-solutions2020/submissions/15448869

https://atcoder.jp/contests/m-solutions2020/submissions/15460557(7/26 追記)

感想

 Nが小さいせいで色々解法が浮かぶのでかえって方針に悩んだ。

解説の方法は「\(3^N N^2\)だしなぁ」と思って切り捨ててしまった。

E - Multiplication 4(AtCoder Beginner Contest 173)

問題リンク

atcoder.jp


問題概要

 \(N\)個の整数\(A_1,A_2,...,A_N\)が与えられる

この中から、ちょうど\(K\)個の要素の積としてありうる値の最大値を求め、

10^9+7で割った余りを答えよ。

制約:

  • \(1 \leq K \leq N \leq 2 \times 10^5\)
  • \(|A_i| \leq 10^9\)
解法

 基本的な戦略としては、Aの要素を絶対値が大きい順にソートしてK個選んで積を計算した後、

それが正ならばそのまま出力、

負ならば

  • 選んだ正の要素のうち絶対値が一番小さいものを除き、選んでいなかった負の要素の中で絶対値が最も大きいものを掛ける
  • 選んだ負の要素のうち絶対値が一番小さいものを除き、選んでいなかった正の要素の中で絶対値が最も大きいものを掛ける

のいずれかで、解の絶対値が大きくなる方を選べば良い。

 

例:

6 3

6 -5 4 -3 2 -1

このとき、まずは\(6 \times (-5) \times 4 = -120\)を解の候補とする。

これは負なので次のどちらかを選択する。

  • 選んだ正の要素のなかで絶対値が最小の4を、選んでいなかった負の要素のなかで絶対値が最大の-3に変える。
  • (上の正負を逆転して)-5を2に変える。

このとき、前者は解の絶対値が\(3/4\)倍され、後者は\(2/5\)倍される。

\(3/4 > 2/5\)なので前者を選ぶ。

最終的な解は\(6 \times (-5) \times (-3) = 90\)。

 

以下の点に注意する。

  • そもそもK個の積が負にしかなりえない場合(サンプル2)。この時は絶対値が最もちいさいものが答えになる。
  • K個の積が負、もしくは0になる場合*1。この時は答えが0になる。
  • 上の「負ならば~」の部分で、選んだ要素が正/負のみ、選んでいなかった要素が正/負のみの場合。
  • 「負ならば~」の部分の解の変化の比較で、小数を使うと誤差の危険がある。\(A/B > C/D \Leftrightarrow AD > BC\)(ただしすべて正)を利用して整数のまま比較する。
  • 負の数の剰余。出力が「0以上10^9+6以下の整数」になるように調整する。
提出

https://atcoder.jp/contests/abc173/submissions/14993932

感想

かなり注意してやったけど、コーナーケースと全然関係ない論理ミスでWA出した。

*1:例えば
4 3
-1 -1 -1 0