CF1285B Just Eat It! - 题解

注:这应该是最快的题解了(总共 \(321\) ms)。

简化题意

判断一个序列是否满足:该序列和严格大于其所有连续子序列和(不包括原序列)

解析

【简化题意】中“严格大于其所有连续子序列和(不包括原序列)”,可以转化成“严格大于非原序列的最大连续子序列和”。所以我们只需要求出“非原序列的最大连续子序列和”并将其与原序列和相比较即可。

求出“非原序列的最大连续子序列和”,我们可以采用 DP 的方法。\(p_i\) 表示从 \(a_1\)\(a_i\) 的最大连续子序列和,那么 \(a_n\) 就表示“最大连续字段和”。

对于每个原序列的每个数 \(a_i\),都需要决定是否将其加入之前的连续子序列

  • 对于“加入”的情况,明显 \(p_i=p_{i-1}+a_i\)

  • 对于“不加入”的情况,那么新的 \(p_i=a_i\)

\(p_i\) 应该取以上两种情况的结果的最大值。即:

\[p_i = \max \begin{cases} p_{i-1}+a_i \\ a_i \end{cases}\]

注意要排除“最大连续子序列为原序列”的情况。我们可以用变量 \(l\) 记录最大连续子序列的左端点,当需要把 \(a_n\) 加入序列且 \(l=1\) 时不予更新。

另外,因为 \(p\)、记录总值和记录最大连续子序列的变量均为多个 \(a\) 数组元素累加的结果,所以需要开 long long

时间复杂度为 \(O(n)\),线性,所有题解中最快

代码

#include<cstdio>
#define max(x,y) x>y?x:y
#define N 200005
using namespace std;

int t,n;
int a[N];
long long p[N];

int main()
{
	scanf("%d",&t);
	for(int I=1;I<=t;I++)
	{
		scanf("%d",&n);
		long long tot=0;	//tot 记录原序列和 
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			tot+=a[i];
		}
		long long ans=-1000000005;			//ans 记录最大连续子序列
		int l=1;
		for(int i=1;i<=n;i++)
		{
			if(p[i-1]+a[i]>a[i])
			{
				if(l==1&&i==n) continue;	//排除“最大连续子序列为原序列”的情况 
				p[i]=p[i-1]+a[i];
			}
			else p[i]=a[i],l=i;		//更改左端点 
			ans=max(ans,p[i]);		//在求 p[i] 时顺便把 ans 也求出来 
		}
		if(tot>ans) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

2022-07-10 13:39 撰写于洛谷,2025-08-29 20:25 迁移至博客园。

posted @ 2025-08-29 20:26  Jerrycyx  阅读(6)  评论(0)    收藏  举报