平方分割

クエリメニュー【累積和・区間和・二次元累積和・二次元区間和・平方分割のバケット・平方分割】

【STEP: 1】コード

reader.on('close', () => {
  const [N, K] = lines[0].split(' ').map(Number);
  const A = [];  // 入力値
  const S = [0];  // A の累積和
  for (let i = 0; i < N; i++) {
    A.push(Number(lines[i + 1]));  // A に 入力値を保存
    S.push(S[i] + A[i]);  // SA の累積和を保存
  }
  for (let i = N + 1; i <= N + K; i++) {
    const Q = Number(lines[i]);
    console.log(S[Q]);  // S[Q] (A[0]〜A[Q-1]の総和) を出力
  }
});
hogeちゃんの画像

 S は 配列 A の 累積和 を保存するための配列です。S[i + 1] の値は S[i] + A[i] になりました。

【STEP: 2】コード

reader.on('close', () => {
  const [N, K] = lines[0].split(' ').map(Number);
  const A = [];
  const S = [0];  // A の累積和
  for (let i = 0; i < N; i++) {
    A.push(Number(lines[i + 1])); // A に 入力値を保存
    S.push(S[i] + A[i]);  // SA の累積和を保存
  }
  for (let i = N + 1; i <= N + K; i++) {
    const [l, r] = lines[i].split(' ').map(Number);
    console.log(S[r] - S[l - 1]);  // A[r - 1] 〜 A[l -1] の総和
   }
});
hogeちゃんの画像
【STEP: 1】と同じく S は 配列 A の 累積和 を保存するための配列です。例えば A[3] から A[5] までの各値の和は S[6]-S[3]でした。

【STEP: 3】コード

reader.on('close', () => {
   const [H, W, N] = lines[0].split(' ').map(Number);
   const A = [];  // HW の2次元配列
   const S = [new Array(W + 1).fill(0)];  // A の2次元累積和 (H + 1) ☓ (W + 1)
   for (let i = 1; i <= H; i++) {
      A.push(lines[i].split(' ').map(Number));  // A に 入力値(配列)を保存
      S.push([0]);  // S に [0] を追加
   }
   for (let h = 0; h < H; h++) {
      for (let w = 0; w < W; w++) {
         S[h + 1][w + 1] = S[h][w + 1] + S[h + 1][w] - S[h][w] + A[h][w];
    // 二次元累積和 = 左の値 + 上の値 - 左斜め上の値 + A[h][w]
      }
   }
   for (let i = H + 1; i <= N + H; i++) {
      const [y, x] = lines[i].split(' ').map(Number);
      console.log(S[y][x]);  // yx (矩形領域)の2次元累積和
   }
});
hogeちゃんの画像

 S は ニ次元配列 A の 累積和 を保存するための 二次元配列 です。 S[h + 1][w + 1] の値は S[h][w + 1] + S[h + 1][w] - S[h][w] + A[h][w]で求めています。

【STEP: 4】コード

reader.on('close', () => {
   const [H, W, N] = lines[0].split(' ').map(Number);
   const A = [];
   const S = [new Array(W + 1).fill(0)];
   for (let i = 1; i <= H; i++) {
      A.push(lines[i].split(' ').map(Number));
      S.push([0]);
   }
   for (let h = 0; h < H; h++) {
      for (let w = 0; w < W; w++) {
         S[h + 1][w + 1] = S[h][w + 1] + S[h + 1][w] - S[h][w] + A[h][w];
      }
   }
   for (let i = H + 1; i <= N + H; i++) {
      const [sy, sx, ey, ex] = lines[i].split(' ').map(Number);
      console.log(S[ey][ex] - S[sy - 1][ex] - S[ey][sx - 1] + S[sy - 1][sx - 1]);
// 矩形領域の累積和 = 右下(領域内) - 左下の左隣(領域外) - 右上の上(領域外) + 左上の斜め上(領域外)
   }
});
hogeちゃんの画像
【STEP: 3】と同様に 2次元累積和 S を生成し 領域内の 右下 – 左下の左隣 – 右上の真上 +左上の斜め上 で 区間和 を求めることができました。console.table(S)S の値を テーブル形式で出力すると 解りやすいと思います。

【STEP: 5】コード

reader.on('close', () => {
   const N = 10000;
   const R = Math.sqrt(N);  // N の平方根
   const A = [];
   for (let i = 0; i < N; i++) {
      A.push(Number(lines[i]));  // 入力値を A に保存
   }
   for (let i = 0; i < R; i++) {
      const a = A.splice(0, R);  // A の値を先頭から R 個切り取り…
      A.push(a);  // 切り取った値(配列)を A の末尾へ挿入
   }
   A.forEach(a => {
      console.log(Math.max(...a));
   });
});
hogeちゃんの画像

Math.sqrt(N)N ( A の 要素数) の 平方根 (100)を求め Aの値を100ずつ切り取った値の最大値 Math.max(...a) を求めて出力しました。

【FINAL】コード

reader.on('close', () => {
   const N = 10000;
   const R = Math.sqrt(N);  // N の平方根
   const K = Number(lines[0]);  // 区間の個数
   const A = [];
   const M = [];
   for (let i = 1; i <= N; i++) {
      A.push(Number(lines[i]));  // 入力値を A に保存
   }
   for (let i = 0; i < R; i++) {
      const a = A.splice(0, R);  // A の値を先頭から R 個切り取り…
      A.push(...a);  // 切り取った値(配列)を A の末尾へ挿入
      M.push(Math.max(...a));
   }
   for (let n = N + 1; n <= N + K; n++) {
      const [l, r] = lines[n].split(' ').map(x => x - 1);
    //console.log(Math.max(...A.slice(l, r + 1)));
      let ans = A[l];
      let i = l;
      while (i <= r) {
         if (i % R === 0 && i + R - 1 <= r) {  // i mod R = 0 且つ i + 99 が 範囲内に収まれば
            ans = Math.max(ans, M[i / R]);  // ans (最大値)を ansM[i / R]の大きい方で更新
            i += R;  // ii + R (100) で更新
          } else {  // i mod R != 0 なら…
            ans = Math.max(ans, A[i]);  // ans (最大値)を ansA[i]の大きい方で更新
            i++;
         }
      }
      console.log(ans);
   }
});
hogeちゃんの画像

【STEP: 5】と同様に配列 M を生成。A の 添字 が R で割り切れて I + 99 が 終点 以下 の場合は ansM[i / R] を比較 し 大きい方で ans を上書き。それ以外は ansA[i] を比較し 同様に ans を上書き

hogeちゃんの画像

余談ですが。。

A[l]〜A[r]までの要素を切り取って最大値を求めてもOKでした。

//console.log(Math.max(...A.slice(l, r + 1)));

コメント