Problem:
最長増加列問題

時は過ぎて太郎君は高校生になりました。
大学生だったお兄さんの影響も受け、コンピューターサイエンスに興味を持ち始めました。

太郎君はコンピューターサイエンスの教科書を読み進め、「最長増加部分列問題」という有名問題があることを知りました。太郎君はこの問題のことを理解しましたが、自分でも類似問題が作れないものかと気になりました。

そこで太郎君は試行錯誤の結果に次のような問題を作りました。

n個の整数で構成される数列Aがある

m-1個の区切りを入れて、数列Aをm個の数列に分解する。なお、分解後のそれぞれの数列は1つ以上の数を必ず含まなければならない。

このm個それぞれの数列内の整数をすべて足し合わせた結果出来上がるm個の数を、元の数列の順に配置すると厳密な増加列になっている(つまり、出来上がる数列はBi < Bi+1をみたす)ようにしたい。

目標は最終的にできる数列Bの長さmを最大化することである。

例えば、数列Aが{5,-4,10,-3,8}の場合を考えてみる。

区切りの位置を表す数列Cを用意し、C={2,4}とする。

このとき、数列Aは(5,-4),(10,-3),(8)の部分に分かれ、これらの内部を足し合わせるとそれぞれ1,7,8となり、出来上がる数列Bは{1,7,8}となる。

太郎君はこの問題について"最長増加列問題"と名付け、これを解くアルゴリズムも考えました。

そして社会人になったあなたにチェックをしてほしいと連絡をしました。

あなたの仕事は、成長した太郎君の作った問題を解くプログラムを作ることです。

チェックするのが仕事なので、m-1個の区切りの位置も出力します。
なお、最適なmに対してこのような区切り方が複数考えられる場合もありますが、このmが正しく出力されていれば、考えられるもののうち一つを出力すればよいです。

Input

改行区切りでn+1個の整数が与えられる。

n
A1
A2
...
An

nは与えられる数列Aの長さを表す

Aiは数列Aのi番目の要素を表す。

Constraints

1≤n≤4000
|Ai|≤ 108　

整数kに対して|k|はkの絶対値を表す

Output

m
C1 C2 .. Cm-1

1行目は一つの整数mを出力する。
mは最終的にできる数列Bの長さを表す。
2行目はm-1個の整数Ciを空白区切りで出力する。
Ciは、数列Aをm個の部分に適切に区切った時のi番目の区切り場所を表す。
区切り場所の定義は図を参照せよ。なお、1≤Ci<n を満たす。
2行目のm-1個の整数列Cは昇順に並んでいなければならないしたがって、i < j ならば
Ci < Cj が成立する。

m=1の場合2行目は空行になる。

数列Cに従って数列Aから数列Bを生成したとき、数列Bが増加列になっていないとき(すなわちBi≥Bi+1となるi(1≤i<m)が存在するとき)、WrongAnswerとなる

Sample Input 1

3
1
2
4

Output for the Sample Input 1

3
1 2

もともと増加列なので、すべての場所に区切りを入れればよい

Sample Input 2

3
2
2
2

Output for the Sample Input 2

2
1

2 2 2は増加列でないので、3つに分割することはできない

Sample Input 3

3
4
2
1

Output for the Sample Input 3

1
(空行)

どう分割しても増加列にはならないため、分割をしない

Solution in C++ (judge verdict: Wrong Answer)

#include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define NUM 4001

struct Edge{
	Edge(short arg_to,ll arg_cost){
		to = arg_to;
		cost = arg_cost;
	}
	bool operator<(const struct Edge &arg) const{
		return cost < arg.cost;
	}
	short to;
	ll cost;
};

struct Info{
	void set(int arg_loc,int arg_rank){
		loc = arg_loc;
		rank = arg_rank;
	}
	short loc,rank;
};

int N;
Info pre[NUM][NUM];
ll table[NUM];
vector<short> MAX_DIVIDE[NUM]; //dp[場所][ある場所から出発するエッジ集合の中でのrank] = 到達までの最大分割数
vector<Edge> G[NUM];


int main(){

	scanf("%d",&N);

	table[0] = 0;

	for(int i = 1; i <= N; i++){
		scanf("%lld",&table[i]);
		table[i] += table[i-1];
	}

	//各場所からエッジを張る
	for(int left = 0; left <= N-1; left++){
		for(int right = left+1; right <= N; right++){
			G[left].push_back(Edge(right,table[right]-table[left]));
		}
		sort(G[left].begin(),G[left].end());
	}

	G[N].push_back(Edge(N+1,HUGE_NUM));

	int L,R,mid;
	int base_rank,start_index,next_loc,next_rank,out_rank;
	ll base_cost;

	//MAX_DIVIDE[場所][相対入次rank] = 左の最大分割数
	for(int loc = 0; loc <= N; loc++){
		MAX_DIVIDE[loc].assign(N-loc+1,0);
	}

	int work[NUM];

	for(int loc = 0; loc <= N-1; loc++){

		//★本来の到着地点★
		for(int i = 0; i < G[loc].size(); i++){
			work[i] = i;
		}

		//ある出発エッジについて、出発するまでの、左方最大分割数を求める
		for(int i = 1; i < G[loc].size(); i++){
			if(MAX_DIVIDE[loc][i] < MAX_DIVIDE[loc][i-1]){
				MAX_DIVIDE[loc][i] = MAX_DIVIDE[loc][i-1];
				work[i] = work[i-1];
			}
		}

		for(int i = 0; i < G[loc].size(); i++){
			next_loc = G[loc][i].to;
			base_cost = G[loc][i].cost;

			//base_costよりも大きい、next_locにおける最小のエッジのインデックスを求める
			L = 0,R = G[next_loc].size()-1,mid = (L+R)/2;
			next_rank = BIG_NUM;

			while(L <= R){
				if(G[next_loc][mid].cost > base_cost){
					next_rank = mid;
					R = mid-1;
				}else{
					L = mid+1;
				}
				mid = (L+R)/2;
			}

			if(next_rank == BIG_NUM)continue;

			if(MAX_DIVIDE[next_loc][next_rank] >= MAX_DIVIDE[loc][i]+1)continue;

			MAX_DIVIDE[next_loc][next_rank] = MAX_DIVIDE[loc][i]+1;
			pre[next_loc][next_rank].set(loc,work[i]);
		}
	}

	int maximum = MAX_DIVIDE[N][0],final_rank = 0;

	if(maximum == 1){
		printf("1\n\n");
		return 0;
	}

	stack<int> S;

	int tmp_loc = N,tmp_rank = final_rank;
	Info info;

	while(pre[tmp_loc][tmp_rank].loc > 0){
		S.push(pre[tmp_loc][tmp_rank].loc);
		info = pre[tmp_loc][tmp_rank];
		tmp_loc = info.loc;
		tmp_rank = info.rank;
	}

	printf("%d\n",maximum);

	if(!S.empty()){
		printf("%d",S.top());
		S.pop();

		while(!S.empty()){
			printf(" %d",S.top());
			S.pop();
		}
	}
	printf("\n");


	return 0;
}