Codeforces Round #783 (Div. 2) D. Optimal Partition

题意:

给定一个由 n 个整数组成的数组 a。 您应该将 a 划分为连续的非空子数组(有 2n-1 种方法可以做到这一点)。

令 s=al+al+1+…+ar。 子数组 al,al+1,…,ar 的值为:

(r−l+1) 如果 s>0,
0 如果 s=0,
-(r-l+1) 如果 s<0。
使用分区可以获得的最大值总和是多少?
输入
输入由多个测试用例组成。 第一行包含一个整数 t (1≤t≤5⋅105) — 测试用例的数量。 测试用例的描述如下。

每个测试用例的第一行包含一个整数 n (1≤n≤5⋅105)。

每个测试用例的第二行包含 n 个整数 a1, a2, ..., an (−109≤ai≤109)。

保证所有测试用例的 n 总和不超过 5⋅105。

输出
为每个测试用例打印一个整数——使用最佳分区可以获得的最大值总和。

想法:

我们可以发现对于这题,我们可以迅速建立dp方程,即dp[i]=max(dp[i],dp[j]+sga(i-j)),为什么是r-l呢,因为我们的方程是寻求第j个前面的情况和j+1,j+2,j+3......等等到r的,所以这些数量合起来只有i-j个。然而我们发现这个方法如果要暴力更新的话,方法是n2的,明显超出了我们想要的时间复杂度。那么我们可以有两种想法:

一种是用三个树状数组分别维护dp[j]-j,fp[j]+j,dp[j],然后分别更新当前的最大值dp[i]变成树状数组1+i,树状数组2-i,树状数组3的最大值,然后一直更新最大值,然而我还没写这种方法。

第二种方法就是我们只需要维护一个dp[j]-j的树状数组就可以了,因为我们发现如果是负数的话,拆开区间一定不可能比总共都是负数的情况更劣,如果是0的话,会拆成全是0的或者一正一负的,这种方法也不可能会比每次通过正数更新的方法更优,因此我们也不需要考虑0的情况,因此我们只要考虑从i到j的这个区间是正数的情况就可以了。

因为我们要做到这一点,所以我们开始要把树状数组的最大值全部初始化为-inf,为什么呢,因为这个-inf的话,可以让一开始的还没加入sum[i]的情况不能进行更新。我们采用树状数组是什么原理呢,我们开始只要离散化掉这个前缀和sum[i],然后对其进行插入操作,也就是把这个sum[i]根据大小关系插入到他该属于的相应位置就行了,为什么可以离散化掉呢,因为我们只要考虑sum[i]的相对应的大小关系,所以我们不用管前缀和的值而将它离散化掉。离散化掉的前缀和我们用来做什么呢,我们用来表示相对应的大小,维护出dp[j]-j的最大值出来,只要我们能找出当前的sum[i]-以前的sum[j]还能大于0的j出来,我们才可以按照区间>0进行更新,可能是本萌新太笨了吧,很多大佬的题解中都没表示出为什么要离散化的原因和为什么要树状数组的原因。然后为什么要维护最大呢,很简单,因为当区间是正数的时候,我们的公式是

dp[i]=dp[j]+(i-j)。我们可以发现dp[j]和j这个是过去就已经需要准备好的用来更新的了,那我们就知道了我们每一次dp[i]更新结束后都应该将它插入sum[i]离散后的哪个位置了,然后我们的dp[j]-j每一次都要找sum[i]-1的位置开始寻找最大值,因为我们要让这个区间>0。然后我们每一次都要试试周边能不能进行更新来顶替负数区间和0的操作,因为如果没有区间大于0的操作的时候,拆开就是最好的选择.

所以代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int Maxn = 5e5+10;
const int INF=1e18;
int a[Maxn],b[Maxn],c[Maxn],h[Maxn],sum[Maxn],dp[Maxn],n;  // a[] 数列的值,h[] 区间最大值
int lowbit(int x){
	return x&(-x);
}
void add (int x, int val) {
	while (x <= n) {
		h[x] = max (h[x], val); 
		x += x&(-x);
	}
}
ll query(int pos){
    ll ret=-INF;
    for(int i=pos;i;i-=lowbit(i))
        ret=max(ret,h[i]);
    return ret;
}
signed main(){
	 ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			b[i]=b[i-1]+a[i];
			sum[i]=sum[i-1]+a[i];
			c[i]=c[i-1]+a[i];
		}
		int m=n;
		sort(b+1,b+1+n);
    	m=unique(b+1,b+1+m)-b-1;//去重操作,返回不同元素的个数
    	for(int i=1;i<=n;++i){
    		sum[i]=lower_bound(b+1,b+1+m,sum[i])-b;
    		h[i]=-INF;
    		dp[i]=0;
    	}
    	h[0]=-INF;
    	dp[0]=0;
    	for(int i=1;i<=n;i++){
    		if(a[i]<0) dp[i]=dp[i-1]-1;
    		else if(a[i]==0) dp[i]=dp[i-1];
    		else dp[i]=dp[i-1]+1;
    		dp[i]=max(dp[i],query(sum[i]-1)+i);
   // 			cout<<dp[i]<<" "<<query(sum[i]-1)<<endl;
    		if(c[i]>0) dp[i]=i;
    		add(sum[i],dp[i]-i);
    	}
    	cout<<dp[n]<<endl;
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值