競技プログラミング練習会

2019 Normal

第2回  累積和, しゃくとり法

担当 :  aotsuki

自己紹介

自己紹介

  • 京都大学理学部 2回生
  • 本名:宮嶋 優大
  • ​​AtCoder:水色
  • 競プロでは主にC++を使用
  • ゲーム制作とweb開発をほんの少しだけ
  • ボードゲームとか麻雀やったりする
    • 好きな人は今度一緒にやりましょう
  • 楽しくやっていきましょう

KMC-ID : aotsuki

slack(内部チャット)

自己紹介

  • 京都大学工学部情報学科2回生
  • 本名:平井 雅人
  • AtCoder: かつては水色だった…(今は緑)
  • CとC++を普段使ってます。
  • aotsukiと交代で回していく予定です。
  • 今回はプログラミング基礎を担当します。
  • 皆さんと共に精進する所存です。よろしくお願いします。

KMC-ID:laft

皆さんの自己紹介もお願いします。

  • 名前(部員の方はKMC-ID)
  • 所属(大学など)
  • 使える言語や使う予定の言語
  • プログラミング経験や競プロの経験(もちろんなくても大丈夫!)
  • 趣味・特技・意気込みなど
  • 何か言いたいこと(あれば)

あくまで一例ですが、下を参考に

宣伝

  • 1日でゲームを作る!
  • ゲームなんて作ったことない...
    • 大丈夫!
    • 部員が1対1でサポートします
  • 楽しい!!!!!!!!!!!!
  • 皆さん参加しましょう!

ラピッドコーディング祭り

  • 日程
    • 第1回 4/28 (日)  9:00 ~ 17:00
    • 第2回 5/5   (日)  9:00 ~ 17:00
  • どちらも同じ内容
    • 都合のいい日に来てね!
    • もちろん両日参加も可!
    • 部員でなくてもOK!

ラピッドコーディング祭り

  • KMC新入生は・・・無料!!!!!
  • ぜひ皆さん参加しましょう!
  • 日時:5月18日(土)
    • 参加登録締切:5月12日 (日)
    • wikiに自分の名前を追加しましょう!

ラピッドコーディング祭り

今日の内容

今日の内容

  • 累積和

  • いもす法

  • しゃくとり法

  • コンテスト!

区間問題

  • 区間
    • 数学と同じ
    • 区間に含まれる要素
      • ある配列に関して、インデックス(添字)が指定された区間内である要素の集合
  • 区間に関する問題
    • ある配列が与えられ、複数の区間に対してなんらかの数値を求める問題
      • 区間に含まれる要素の和 など

区間

  • 区間に関する問題で使える手法
    • 累積和
    • いもす法
    • しゃくとり法
    • セグメント木
    • Binary Indexed Tree (BIT)
    • 平方分割
    • ...

区間問題

累積和

  • 累積和とは
    • 連続する区間の和を高速で求める手法
    • 先頭から各要素までの和(=累積和)を利用する
    • 実は和以外にも使えるらしい
      • (逆演算が存在すれば)​

累積和

  • 使える状況                  
    • 区間和を大量に求めたい時     
    • etc...

例題

長さ     の数列 {      } が与えられる。

それに対し      個のクエリが与えられる。

各クエリでは2整数                             が与えられるので、

区間の和                                         を求めよ。

制約

問題文

実行時間制限: 2 sec  / メモリ制限: 1024 MB

1\leqq N \leqq 10^5
1\leqq Q \leqq 10^5
N
{a_n}
Q
i,j
(1\leqq i \leqq j \leqq N)
a_i+a_{i+1}+a_{i+2}+\dots+a_{j}
  • 毎回言われた通り i から j までの要素を足していく
    • i = 1 , j = N の時、N 回足さなければいけない
    • つまりクエリごとに最悪   かかる
    • これが Q 回繰り返されると
    • よって計算量は最悪

間に合わない!!

解法1:愚直解

O(N)
O(QN)
10^{10}
  • 準備として先頭から各要素までの部分和(=累積和)を求めておく
  • その部分和の配列を{     }とし、もとの配列を{    }とする
  •  S[i] := 半開区間 [0, i) における部分和
  • つまり

解法2:累積和

S_i=\sum_{k=0}^{i-1} a_k
{S_n}
{a_n}

解法2:累積和

{S_0}
{a_0}
{S_1}
{S_2}
{0}
{\vdots}
{S_i}
{\vdots}
{S_{N}}
{a_0}+{a_1}
{a_0}+{a_1}+\dots+{a_{i-1}}
{a_0}+{a_1}+\dots+\dots+{a_{N-1}}
(={S_1}+{a_1})
(={S_0}+{a_0})
(={S_{i-1}}+{a_{i-1}})
(={S_{N-1}}+{a_{N-1}})

解法2:累積和

S[0]=0;
for(int i=1;i<=N;i++){
    S[i]=S[i-1]+a[i-1];
}

{     }は以下のように求めることができる

計算量は

{S_n}
O(N)

解法2:累積和

  • この{      }を使って、閉区間[ i , j ]の要素の和を求めたい
  •      から     までの和なので、     とすれば求められる
  • 計算量は
{a_i}
{a_j}
{S_n}
{S_{j+1}}-{S_{i}}
O(1)

解法2:累積和

なぜなら

{S_i}
{S_{j+1}}
{a_0}+{a_1}+\dots+{a_{i-1}}
{a_0}+{a_1}+\dots+{a_{i-1}}+{a_{i}}+\dots+{a_{j}}

{a_{i}}+\dots+{a_{j}}
{S_{j+1}}-{S_{i}}

解法2:累積和

  • まず累積和を求めるのに 
  • 一つのクエリを処理するのに 
  • それを Q 回行うので、クエリの処理全体で
  • よって、全体の計算量は
  • すなわち     程度
  • これなら十分間に合う!
O(N)
O(1)
O(Q)
O(N+Q)
10^5

二次元累積和

  • さっきは一次元領域だった
    • ​区間の和を高速に求めた
  • 二次元領域でも累積和が使える
    • 長方形領域内の和を高速に求める
  • 考え方はさっきとほとんど同じ

二次元累積和

累積和の準備

全ての行(横方向)に対して累積和を取る

Step 2で求めた累積和を用いて、さらに全ての列(縦方向)に対して累積和を取る

出来上がった二次元累積和を用い、長方形領域内の和を求める

二次元累積和

Step 1​​

Step 2

Step 3

 

Step 4

使い方

Step 1​​ 累積和の準備

  • もとの二次元配列よりも縦横それぞれ1つだけ大きい配列を作っておく
  • 最初の行・最初の列を 0 で埋める
    • ​半開区間を表現するため

Step 1​​ 累積和の準備

{a} 0 1 2 3 4 5
0 5 0 -3 0 7 2
1 2 8 -1 3 0 -3
2 4 8 -3 8 8 5
3 1 1 7 -3 6 1
4 -2 5 6 -3 0 -2
5 -4 3 2 0 -1 2

これが元の配列a だとすると...

Step 1​​ 累積和の準備

{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

こうなります(この配列を説明上a'とする)

Step 2​ 全ての行に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

Step 2​ 全ての行に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

Step 2​ 全ての行に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

Step 2​ 全ての行に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

Step 2​ 全ての行に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

Step 2​ 全ての行に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

Step 2​ 全ての行に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

Step 2​ 全ての行に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 2 10 9 12 12 9
3 0 4 12 9 17 25 30
4 0 1 2 9 6 12 13
5 0 -2 3 9 6 6 4
6 0 -4 -1 1 1 0 2

Step 3 全ての列に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 2 10 9 12 12 9
3 0 4 12 9 17 25 30
4 0 1 2 9 6 12 13
5 0 -2 3 9 6 6 4
6 0 -4 -1 1 1 0 2

Step 3 全ての列に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 2 10 9 12 12 9
3 0 4 12 9 17 25 30
4 0 1 2 9 6 12 13
5 0 -2 3 9 6 6 4
6 0 -4 -1 1 1 0 2

Step 3 全ての列に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 7 10 9 12 12 9
3 0 4 12 9 17 25 30
4 0 1 2 9 6 12 13
5 0 -2 3 9 6 6 4
6 0 -4 -1 1 1 0 2

Step 3 全ての列に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 7 10 9 12 12 9
3 0 4 12 9 17 25 30
4 0 1 2 9 6 12 13
5 0 -2 3 9 6 6 4
6 0 -4 -1 1 1 0 2

Step 3 全ての列に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 7 10 9 12 12 9
3 0 11 12 9 17 25 30
4 0 1 2 9 6 12 13
5 0 -2 3 9 6 6 4
6 0 -4 -1 1 1 0 2

Step 3 全ての列に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 7 10 9 12 12 9
3 0 11 12 9 17 25 30
4 0 1 2 9 6 12 13
5 0 -2 3 9 6 6 4
6 0 -4 -1 1 1 0 2

Step 3 全ての列に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 7 10 9 12 12 9
3 0 11 12 9 17 25 30
4 0 12 2 9 6 12 13
5 0 -2 3 9 6 6 4
6 0 -4 -1 1 1 0 2

Step 3 全ての列に対して累積和を取る

{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 7 15 11 14 21 20
3 0 11 27 20 31 46 50
4 0 12 29 29 37 58 63
5 0 10 32 38 43 64 67
6 0 6 31 39 44 64 69
{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 7 15 11 14 21 20
3 0 11 27 20 31 46 50
4 0 12 29 29 37 58 63
5 0 10 32 38 43 64 67
6 0 6 31 39 44 64 69

さて、出来上がった配列       は何を表しているのか?

S_{ij}

例えば              から       までの和だった

S_{14}
a^{\prime}_{11}
a^{\prime}_{14}
{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

      は       に      から      までの和を足したものだった

S_{24}
a^{\prime}_{21}
a^{\prime}_{24}
S_{14}
+S_{14}
{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

つまり             から       までを足したもの!

S_{24}
a^{\prime}_{11}
a^{\prime}_{24}
{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

      は       に      から      までの和を足したものだった

S_{34}
a^{\prime}_{31}
a^{\prime}_{34}
S_{24}
+S_{24}
{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

つまり             から       までを足したもの!

S_{34}
a^{\prime}_{11}
a^{\prime}_{34}
{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
{S} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 5 2 2 9 11
2 0 7 15 11 14 21 20
3 0 11 27 20 31 46 50
4 0 12 29 29 37 58 63
5 0 10 32 38 43 64 67
6 0 6 31 39 44 64 69

さて、出来上がった配列       は何を表しているのか?

S_{ij}

=>       から      までの総和を表している

a_{ij}
a_{11}

Step 4 長方形領域内の和を求める

例えば、以下の領域の和を求めたい

{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2

Step 4 長方形領域内の和を求める

この領域の総和(      )から...

現在の範囲

{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
{a} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
S_{53}

Step 4 長方形領域内の和を求める

この領域の総和(      )を引き...

現在の範囲

{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
{a} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
S_{51}

Step 4 長方形領域内の和を求める

さらにこの領域の総和(      )も引き...

現在の範囲

{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
{a} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
S_{13}

Step 4 長方形領域内の和を求める

2回引かれたこの領域の総和(      )を足すと...

現在の範囲

{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
{a} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
S_{11}

Step 4 長方形領域内の和を求める

この領域の総和が求まる!

{a'} 0 1 2 3 4 5 6
0 0 0 0 0 0 0 0
1 0 5 0 -3 0 7 2
2 0 2 8 -1 3 0 -3
3 0 4 8 -3 8 8 5
4 0 1 1 7 -3 6 1
5 0 -2 5 6 -3 0 -2
6 0 -4 3 2 0 -1 2
  • つまり、     の総和を求めたかったら...​​

二次元累積和

a^{\prime}_{ij} \dots a^{\prime}_{iJ}\\ \\[-1.5mm]\vdots \hspace{1.5mm} \ddots \hspace{1.5mm} \vdots\\ \\[-1.5mm] a^{\prime}_{Ij} \dots a^{\prime}_{IJ}
S_{IJ}-S_{{I}{j-1}}-S_{{i-1}{J}}+S_{{i-1}{j-1}}

で求まる!  (        )

O(1)
  • 計算量は...   (縦 W, 横 H, クエリの数 Qとする)
    • Step 1 で 
    • Step 2 で
    • Step 3 で
    • Step 4 で
  • よって全体の計算量は

二次元累積和

O(W\times H)
O(H\times W)
O(1\times Q)
O(W\times H+Q)

いもす法

  • いもす法とは
    • 累積和の応用
    • 「最後に累積和をとれば求めたい数列になる」ようにすることで、区間に対する書き換えを 高速に実行できる

いもす法

  • 使える状況
    • 区間に対する書き換えを大量に行いたい時  
    • etc...

例題

あなたは店を経営している。訪れた     人の客について入店時刻     と出店時刻     が与えられる。

同時刻に店にいた客の数の最大値はいくらか。

同時刻に入店や出店がある場合、まず出店から行われるとする。

制約

問題文

実行時間制限: 2 sec  / メモリ制限: 1024 MB

1\leqq N \leqq 10^5
1\leqq T \leqq 10^5
0\leqq {s_i} < {e_i} \leqq T
N
s_i
e_i
  • 長さ     の配列    を用意する
  •    は時刻    に店にいたお客さんの数とする
  •    が求まれば、同時刻に店にいた客の数の最大値が求まる
  • よって    を求めよう!

例題

T+1
C_t
C_t
t
C_t
C_t

方針

  •     と     が与えられるごとに、      の範囲の   に 1 を足す
    •  を含まないのは出店が先に行われるから
  • 最悪     回の足し算を    人分する必要があるので、計算量は
  • よって   となるため間に合わない

解法1 愚直解

C_t
s_i
e_i
s_i\leqq t < e_i
e_i
O(TN)
T
N
{10}^{10}
  • 新しい配列   を用意して、    と     が与えられるごとに、      を +1して      を -1 する
  • 全て終わったら累積和をとり、出来た配列を     とする
  • これなら       で        くらいなので大丈夫
  • 詳しくは いもす法解説ページ を参照

解法2 いもす法

s_i
e_i
P_{s_i}
P_{e_i}
O(N+T)
{10}^{5}
C_{t}
P_{t}
  • なぜこれで求まるのか
    • 例えば   に +1すると、累積和を取った後の配列では      全てに +1される
    • 同様に   に -1すると、累積和を取った後の配列では      全てに -1される
  • つまり   に +1、  に -1すると、累積和を取った後の配列では      に +1される
    • 店にいた時刻の区間全てに +1されている

解法2 いもす法

P_{s_i}
P_{e_i}
C_{s_{i}}\cdots C_{T}
C_{e_{i}}\cdots C_{T}
P_{s_i}
P_{e_i}
C_{s_{i}}\cdots C_{e_{i}-1}

しゃくとり法

  • しゃくとり法とは
    • 連続する区間の和を高速で求める手法
    • 区間の右端を伸ばしたり左端を縮めたりする
      • 尺取り虫に似ている
    • 区間の幅を固定する方法と、伸ばせるだけ伸ばす方法がある

しゃくとり法

  • 使える状況                   
    • 区間和を大量に求めたい時     
    • etc...

例題1

長さ     の数列 {      } が与えられる。

その数列の連続する     個の整数の和の最大値を出力せよ。

-10^4\leqq a_i \leqq 10^4

制約

問題文

実行時間制限: 2 sec  / メモリ制限: 1024 MB

1\leqq n \leqq 10^5
1\leqq k \leqq n
n
{a_n}
k
  • 和をとる開始点    を定める
  • そこから    個の和を順に足して求める (         
  • 全体で              となる
  • これは最悪           で、間に合わなさそう

解法1 愚直解

i
O(k)
O(nk)
k
10^{10}
  • 前処理として累積和を求めておく (         ) 
  • 和をとる開始点    を定める
  • そこから    個の和を累積和で求める (         ) 
  • 全体で          となる
  • 十分間に合う

解法2 累積和

i
O(n)
k
O(1)
O(n)
  • まず、    から     までの和をとる  (         )
  • 次に区間を一つ先にずらし、   から         までの和を求めよう
    • 先の和から     を引いて        を足せばよい (         )
    • 先頭を1つ伸ばして、末尾を一つ縮める
  • このようにして一つずつずらしていき、最大値を更新していく  (          )
  • 全体で          となる
  • わずかに累積和より高速

解法3 しゃくとり法

a_k
O(k)
a_0
O(n)
a_1
a_{k+1}
a_0
a_{k+1}
O(1)
O(n)

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

 現在の和:3

和の最大値:3

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

     現在の和:ー

和の最大値:3

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

     現在の和:ー

和の最大値:3

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

 現在の和:11

和の最大値:11

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

   現在の和:ー

和の最大値:11

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

   現在の和:ー

和の最大値:11

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

  現在の和: 9

和の最大値:11

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

   現在の和:ー

和の最大値:11

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

   現在の和:ー

和の最大値:11

解法3 しゃくとり法

2 5 -4 10 3 -5

例:n=6, k=3

  現在の和: 8

和の最大値:11

  • この問題では累積和を使っても、しゃくとり法を使ってもどちらでも良い
  • 先に思いついた方を使って大丈夫

解法3 しゃくとり法

例題2

長さ     の数列 {     } が与えられる。

連続部分列で総和が     以上になるもののうち、長さが最小となるものの長さを出力せよ。

1\leqq a_i \leqq 10^9

制約

問題文

実行時間制限: 2 sec  / メモリ制限: 1024 MB

1\leqq n \leqq 10^5
1\leqq S \leqq 10^9
n
S
{a_n}
  • 左端    と右端    を決める
    • 決め方は      通りくらい
  • 決めたら     から     までを愚直に足し算する
    • 最悪長さ    になるので
  • 全体で            となる
    •          なのでまず間に合わない

解法1 愚直解

r
O(n)
O(n^3)
l
10^{15}
n^2
a_l
a_r
n

解法1 愚直解

ここは改良できる

  • 左端    と右端    を決める
    • 決め方は      通りくらい
  • 決めたら     から     までを愚直に足し算する
    • 最悪長さ    になるので
  • 全体で            となる
    •          なのでまず間に合わない
r
O(n)
O(n^3)
l
10^{15}
n^2
a_l
a_r
n
  • 事前に累積和を計算しておく (         )
  • 左端    と右端    を決める
    • 決め方は       通りくらいある
  • 累積和で和を求める (        )
  • 全体で            となる
    •         なので、まだ厳しい

解法2 累積和

r
O(n)
O(n^2)
l
10^{10}
n^2
O(1)
  • 事前に累積和を計算しておく (         )
  • 左端    と右端    を決める
    • 決め方は       通りくらいある
  • 累積和で和を求める (        )
  • 全体で            となる
    •         なので、まだ厳しい

解法2 累積和

r
O(n)
O(n^2)
l
10^{10}
n^2
O(1)

ここも改良できる

  • 事前に累積和を計算しておく (         )
  •               より和は 単調増加
    • よって  を越える位置を二分探索できる!
  • 左端    を決める
    • 決め方は     通りある
  •     を越える最小の    を二分探索で見つける (              )
  • 全体で                    となる
    •                             なので、これなら間に合う

解法3 累積和+二分探索

S
O(n)
{a_i}>0
l
(5\log 10)\times 10^5
n
O(n\log n)
O(\log n)
S
r
  • 本番でこの方法が思い付けたなら 大正解 です
  • 二分探索って何?
    • 来週やります!お楽しみに!

解法3 累積和+二分探索

  • 左端    と右端    を決める
    • 決め方は      通りくらい
  • 決めたら     から     までを愚直に足し算する
    • 最悪長さ    になるので
  • 全体で            となる
    •          なのでまず間に合わない

解法1 愚直解

r
O(n)
O(n^3)
l
10^{15}
n^2
a_l
a_r
n
  • 左端    と右端    を決める
    • 決め方は      通りくらい
  • 決めたら     から     までを愚直に足し算する
    • 最悪長さ    になるので
  • 全体で            となる
    •          なのでまず間に合わない

解法1 愚直解

r
O(n)
O(n^3)
l
10^{15}
n^2
a_l
a_r
n

ここを改良しよう

解法4 愚直解(改良版)

  • 左端    を決める
    • 決め方は     通り
  • 決めたら     から 1 つずつ右に伸ばしていく
    • 和が    を越えたらストップ
    • 愚直に足し算をすると最悪長さ    になるので
  • 全体で            となる
    •          なのでまだ間に合わない
O(n^2)
O(n)
l
10^{10}
n
a_l
n
S

解法4 愚直解(改良版)

今度はここを改良しよう

  • 左端    を決める
    • 決め方は     通り
  • 決めたら     から 1 つずつ右に伸ばしていく
    • 和が    を越えたらストップ
    • 愚直に足し算をすると最悪長さ    になるので
  • 全体で            となる
    •          なのでまだ間に合わない
O(n^2)
O(n)
l
10^{10}
n
a_l
n
S

解法5 しゃくとり法

  • 最初は     のみ長さ 1 の区間で考える ( 左端l=0, 右端r=0 )
  • 区間の和の大きさにより場合分け
    • 現在の区間の和が    未満 → 右を1つ伸ばして和を更新 ( r++ )
    • 現在の区間の和が    以上 → 左を1つ縮めて和を更新 ( l++ )
  • 更新後、和が    以上だったら現在の長さで長さの最小値を更新
  • ​これを繰り返して一番右を越えようとしたところで終了
  • 全体で          で解ける
    •    なので余裕
O(n)
S
10^{5}
a_0
S
S
4 2 5 1 7 3

例:n=6, S=11

         現在の和:4 < S

長さの最小値:ー

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

   現在の和:6

長さの最小値:ー

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

         現在の和:6 < S

長さの最小値:ー

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

     現在の和:11

長さの最小値:ー

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

             現在の和:11    S

長さの最小値:3

\geqq

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

     現在の和:7

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

           現在の和:7 < S

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

    現在の和:8

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

           現在の和:8 < S

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

       現在の和:15

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

              現在の和:15     S

長さの最小値:3

\geqq

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

       現在の和:13

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

              現在の和:13     S

長さの最小値:3

\geqq

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

     現在の和:8

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

           現在の和:8 < S

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

       現在の和:11

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

              現在の和:11     S

長さの最小値:3

\geqq

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

       現在の和:10

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

             現在の和:10 < S

長さの最小値:3

解法5 しゃくとり法

4 2 5 1 7 3

例:n=6, S=11

          現在の和:終了

長さの最小値:3

解法5 しゃくとり法

解法5 しゃくとり法

  • 最初は     のみ長さ 1 の区間で考える ( 左端l=0, 右端r=0 )
  • 区間の和の大きさにより場合分け
    • 現在の区間の和が    未満 → 右を1つ伸ばして和を更新 ( r++ )
    • 現在の区間の和が    以上 → 左を1つ縮めて和を更新 ( l++ )
  • 更新後、和が    以上だったら現在の長さで長さの最小値を更新
  • ​これを繰り返して一番右を越えようとしたところで終了
  • 全体で          で解ける
    •    なので余裕
O(n)
S
10^{5}
a_0
S
S

解法5 しゃくとり法

  • なぜ   なのか?
    • はじめは
    • 最後は
    • 一回の操作で    か    どちらかを1増やす
    • つまり操作の回数は最大で    回
    • 定数は無視するので
O(n)
l
r
l\leqq n-1, r=n-1
l=0, r=0
2n
O(n)

コンテスト

  • コンテストはAizu Online Judgeで行います
  • まだしてない人は会員登録をしましょう
  • 下記のURLにアクセスしてください

コンテスト

http://judge.u-aizu.ac.jp/onlinejudge/index.jsp?lang=ja

もしくは

「 AOJ 」で検索

  • 下記のURLにアクセスしてください
  • パスワードは "y" です
  • コンテスト開始までしばしお待ちください

コンテスト

https://onlinejudge.u-aizu.ac.jp/beta/room.html#kmc2019_n_2

もしくは

http://bit.ly/2UTt4Of

もしくは

部内wikiから

終わり!

解説して欲しいものありますか?

ごはん食べよう!!!

(おごりは資金が底を尽きたため終了しました)

参考文献

第2回:累積和, しゃくとり法

By procon2019

第2回:累積和, しゃくとり法

発表日時 2019年4月19日(金) 18:30-21:00 https://onlinejudge.u-aizu.ac.jp/beta/room.html#kmc2019_n_2

  • 701