题目

展开
题目背景
loidc来到了NOI的赛场上,他在那里看到了好多神犇。

题目描述
神犇们现在正排成一排在刷题。每个神犇都有一个能力值p[i]。loidc认为坐在附近的金牌爷能力参差不齐非常难受。于是loidc便想方设法对神犇们进行人道主义合并。

loidc想把神犇的能力值排列成从左到右单调不减。他每次可以选择一个神犇,把他合并到两侧相邻的神犇上。合并后的新神犇能力值是以前两位犇的能力值之和。每次合并完成后,被合并的两个神犇就会消失。合并后的新神犇不能再分开(万一他俩有女朋友咋办)因此每次合并后神犇的总数会减1.

loidc想知道,想治好他的强迫症需要合并多少次

输入格式
第一行一个整数 n。

第二行 n 个整数,第 i 个整数表示 p[i]。

输出格式
loidc需要合并的次数

输入输出样例
输入 #1复制
8
1 9 9 4 1 2 2 9
输出 #1复制
3
说明/提示
对于 50%的数据,0< n <=5000。

对于 100%的数据,0< n <=200000,0< p[i] <=2147483647,p 均为随机生成。

思路

f[i]表示前 i 个数最少合并的次数。g[i]表示前 i 个数在满足合并了f[i]次的条件下最后一组的总和最小是多少。sum[i]是前缀和。
然后 f[i]=f[j]+i-j-1;
g[i]=sum[i]-sum[j];

代码

#include<bits/stdc++.h>
#define int long long
using namespace std; 
const int N=2e5+77; 
int head,tail=1,n,f[N],pre[N],sum[N],q[N]; 
signed main()
{
	cin>>n; 
	int x; 
	for(int i=1; i<=n; ++i)
		cin>>x,sum[i]=sum[i-1]+x; 
	for(int i=1; i<=n; ++i) 
	{
		while(head+1<tail&&sum[i]>=sum[q[head+1]]+pre[q[head+1]])
			++head; 
		f[i]=f[q[head]]+1; 
		pre[i]=sum[i]-sum[q[head]]; 
		while(head<tail&&sum[q[tail-1]]+pre[q[tail-1]]>sum[i]+pre[i])
			--tail; 
		q[tail++]=i; 
	}
	cout<<n-f[n];
}