Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

バームクーヘン(Baumkuchen)

JOI 君は妹のJOI 子ちゃんとJOI 美ちゃんと一緒におやつを食べようとしている．今日のおやつは3 人の大好物のバームクーヘンだ．

バームクーヘンは下図のような円筒形のお菓子である．3 人に分けるために，JOI 君は半径方向に刃を3回入れて，これを3 つのピースに切り分けなければならない．ただしこのバームクーヘンは本物の木材のように固いので，刃を入れるのは簡単ではない．そのためこのバームクーヘンにはあらかじめ $N$ 個の切れ込みが入っており，JOI 君は切れ込みのある位置でのみ切ることができる．切れ込みに1 から $N$ まで時計回りに番号をふったとき，$1 \leq i \leq N - 1$ に対し， $i$ 番目の切れ込みと$i + 1$ 番目の切れ込みの間の部分の大きさは $A_i$ である．また $N$ 番目の切れ込みと1 番目の切れ込みの間の部分の大きさは $A_N$ である．

図1: バームクーヘンの例 $N = 6, A_1 = 1, A_2 = 5, A_3 = 4, A_4 = 5, A_5 = 2, A_6 = 4$

課題

切れ込みの個数 $N$ と，各部分の大きさを表す整数 $A_1,...,A_N$ が与えられる．バームクーヘンを3 つに切り分けたときの，最も小さいピースの大きさの最大値を出力するプログラムを作成せよ．

入力

標準入力から以下のデータを読み込め．

1 行目には，整数 $N$ が書かれている．これはバームクーヘンに $N$ 個の切れ込みがあることを表す．

続く$N$ 行のうちの $i$ 行目$(1 \leq i \leq N)$ には，整数 $A_i$ が書かれている．これは $i$ 番目の切れ込みと $i + 1$ 番目の切れ込みの間の部分 ($i = N$ のときは $N$ 番目の切れ込みと1 番目の切れ込みの間の部分) の大きさが $A_i$ であることを表す．

出力

標準出力に，バームクーヘンを3 つに切り分けたときの，最も小さいピースの大きさの最大値を表す整数を1 行で出力せよ．

制限

すべての入力データは以下の条件を満たす．

$3 \leq N \leq 100000$

$1 \leq A_i \leq 1000000000 (1 \leq i \leq N)$

入出力例

入力例 1

6
1
5
4
5
2
4

出力例 1

6

図2: 1 番目の切れ込みと3 番目の切れ込みと5 番目の切れ込みで切るのが最善である．

入力例 2

30
1
34
44
13
30
1
9
3
7
7
20
12
2
44
6
9
44
31
17
20
33
18
48
23
19
31
24
50
43
15

出力例 2

213

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;

namespace Baumkuchen {
	class Baumkuchen {
		static void Main(string[] args) {		
			int N = int.Parse(Console.ReadLine());
			long[] v_list = new long[N + 1];
			v_list[0] = 0;
			for (int n = 1; n < N + 1; n++) {
				v_list[n] = v_list[n - 1] + long.Parse(Console.ReadLine());
			}
			Baumkuchen baumkuchen = new Baumkuchen(v_list);
			Console.WriteLine(baumkuchen.GetAnwser());
		}

		readonly long[] v_list;
		readonly int N;
		readonly double tripartition;
		public Baumkuchen(long[] _v_list) {
			N = _v_list.Length - 1;
			v_list = _v_list;
			tripartition = v_list[N] / 3.0;
		}

		public long GetAnwser() {
			long interm_maxV = -1;
			int n0_max = findLeftIndex(0, N, tripartition);
			for (int n0 = 0; n0 <= n0_max; n0++){
				long tmp = searchOne(n0);
				if (tmp > interm_maxV) interm_maxV = tmp;
			}
			return interm_maxV;
		}

		private long searchOne(int n0) {
			int n1_left = findLeftIndex(n0, N, tripartition + v_list[n0]);
			int n2_left = findLeftIndex(n1_left, N, 2 * tripartition + v_list[n0]);
			long V_ll = getMinV(n0, n1_left, n2_left);
			long V_lr = getMinV(n0, n1_left, n2_left + 1);
			long V_rl = getMinV(n0, n1_left + 1, n2_left);
			long V_rr = getMinV(n0, n1_left + 1, n2_left + 1);
			return Math.Max(Math.Max(V_ll, V_lr), Math.Max(V_rl, V_rr));
		}

		private long getMinV(int n0, int n1, int n2) {
			long V0 = v_list[n1] - v_list[n0];
			long V1 = v_list[n2] - v_list[n1];
			long V2 = v_list[n0] - v_list[n2] + v_list[N];
			return Math.Min(Math.Min(V0, V1), V2);
		}

		private int findLeftIndex(int from_n, int to_n, double target) {
			int n_left = from_n;
			int n_right = to_n;
			while (n_right - n_left !=1) {
				int n_mid = (n_right + n_left) / 2;
				if (v_list[n_mid] <= target) n_left = n_mid;
				else n_right = n_mid;
			}
			return  n_left;
		}
	}
}