geam1113’s diary

主にAtcoderのコンテストの備忘録を書きます。AtCoder緑と水色を行ったり来たりしています。

ABC239 参加記録

コンテスト中AC:A〜E

B - Intenger Division

以前、このような自作関数を作っていました。

template<typename T> 
T Floor(T a, T b) {
    if ((a<0 && b<0) || (a>0 && b>0)) return a/b;
    else return (Abs(a)+Abs(b)-1)/Abs(b)*-1LL;
}

これを使うことで、Floor(X,10LL)で求められました。
提出コード

E - Subtree K-th Max

Kが小さいことから、公式解説の想定解を思いつきつつも、計算量の見積もりがよくわからなかったので、異なる解法で解きました。
オイラーツアーの部分木クエリと同じ考え方となります。

まず、一回のDFSで以下を記録しておきます。

  • 頂点u_iのpre-order順。これをId_uとします。
  • X_iを頂点のpre-order順に並べたもの。これを配列Aとします。
  • 部分木に属する頂点数。これをdp_uとします。

このようにすると、i番目のクエリは、

  • Id_{V_i} \leq j \lt Id_{V_i}+dp_{V_i}を満たすA_jのうちK_i番目に大きい数を求めよ

という問題となります。 入力例1を例にします。以下はDFS実行後の一例です。

A = \{1,4,2,5,3\}
 Id = \{1,3,5,2,4\}
 dp = \{5,3,1,1,1\}

ここで、2個目のクエリ、頂点2を根とした時の1番目に大きい数を求めてみます。
対象となるA_j区間は、3\leq j \lt 3+3 \rightarrow 3\leq j \lt 6となるので、A_3,A_4,A_5から1番目に大きい値を計算します。
2,3,5なので、答えは5です。

ここで、K番目に大きい値を高速に得る必要があります。
これはウェーブレット行列により可能となります。

Aのウェーブレット行列を求めておます。ウェーブレット行列には以下の関数があります。

  • quantile(s,e,r) : 半開区間[s,e)のうち、r+1番目に小さい値を返す。計算量はO(logX_{max})。(但し、X_{max}X_iの最大値)

これを用いると、区間のサイズがdp_{V_i}であることから、quantile(Id_{V_i},\ Id_{V_i}+dp_{V_i},\ dp_{V_i} - K)として解を得られます。

計算量は、木なのでDFSにO(N)かかります。また、ウェーブレット行列の構築にO(NlogX_{max})
ウェーブレット行列で各クエリに答えるのにO(QlogX_{max})かかります(多分)。

提出コード