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)

#include <iostream>
#include <algorithm>

using namespace std;
typedef unsigned long long ull;

ull PickLargestMinimumPiece(const ull _total_size, const ull _piece1_s, const ull _piece1_l, const ull _piece3_s, const ull _piece3_l){
	ull tmp = min(_piece1_s, _piece3_s);
	
	ull piece2 = _total_size - _piece1_s - _piece3_l;
	tmp = max(tmp, min(_piece1_s, piece2));

	piece2 = _total_size - _piece1_l - _piece3_s;
	tmp = max(tmp, min(_piece3_s, piece2));

	piece2 = _total_size - _piece1_l - _piece3_l;
	tmp = max(tmp, piece2);

	return tmp;
}

void CalcPiece1(int *_i2, ull *_piece1_s, ull *_piece1_l, const int _i1, const int _N, const ull _one_third_size, const int *_A){
	for (; *_i2 < _i1 + _N - 2;){
		*_piece1_s = *_piece1_l;
		*_piece1_l += _A[(*_i2)%_N];
		(*_i2)++;
		if (*_piece1_l > _one_third_size) break;
	}
}

int main(){
	cin.tie(0);
	ios::sync_with_stdio(false);

	int N;	//切れ込みの個数
	cin >> N;

	int *A = new int[N];	//各ピースの大きさを格納する配列
	ull total_size = 0;	//バームクーヘンの大きさ
	int i1_ini = 0;
	int A_max = 0;
	for(int i=0; i<N; i++){
		cin >> A[i];
		total_size += A[i];
		if (A[i] > A_max){
			A_max = A[i];
			i1_ini = i;
		}
	}
	ull one_third_size = total_size / 3;
	ull answer = 0;
	if (A_max > one_third_size){
		ull half_size = (total_size - A_max) / 2;
		ull tmp = 0;
		ull piece2 = 0;
		for (int i3 = i1_ini + 1; i3 < i1_ini + N - 1; i3++){
			piece2 = tmp;
			tmp += A[i3%N];
			if (tmp > half_size) break;
		}
		ull piece3 = total_size - A_max - tmp;
		answer = max(piece2, piece3);
		cout << answer << endl;
		delete[] A;
		return 0;
	}

	int i2 = i1_ini;
	ull piece1_s = 0;
	ull piece1_l = 0;
	CalcPiece1(&i2, &piece1_s, &piece1_l, i1_ini, N, one_third_size, A);
	const int i1_fin = i2;

	int i3 = i2;
	ull piece3_s = total_size - piece1_l;
	ull piece3_l = 0;
	for (; i3 < i1_ini + N - 1;){
		piece3_l = piece3_s;
		piece3_s -= A[i3%N];
		i3++;
		if (piece3_s <= one_third_size) break;
	}

	answer = PickLargestMinimumPiece(total_size, piece1_s, piece1_l, piece3_s, piece3_l);

	for (int i1 = i1_ini + 1; i1 < i1_fin; i1++){
		int A_tmp = A[(i1 - 1) % N];
		piece1_l -= A_tmp;
		if (piece1_l > one_third_size) piece1_s -= A_tmp;
		else{
			/*
			for (; i2 < i1 + N - 2;){
				piece1_s = piece1_l;
				piece1_l += A[i2%N];
				i2++;
				if (piece1_l > one_third_size) break;
			}//*/
			CalcPiece1(&i2, &piece1_s, &piece1_l, i1, N, one_third_size, A);
		}

		piece3_s += A_tmp;
		if (piece3_s <= one_third_size) piece3_l += A_tmp;
		else{
			for (; i3 < i1 + N - 1;){
				piece3_l = piece3_s;
				piece3_s -= A[i3%N];
				i3++;
				if (piece3_s <= one_third_size) break;
			}
		}

		answer = max(answer, PickLargestMinimumPiece(total_size, piece1_s, piece1_l, piece3_s, piece3_l));
	}

	cout << answer << endl;

	delete[] A;

	return 0;
}