Codeforces Round #783 (Div. 2)-D-Optimal Partition(树状数组,离散化)

题目链接:Problem - B - Codeforces

大致题意:给定一个序列,你可以将序列划分为若干个连续非空子数组。若子数组内数字和大于0,则该子数字权值为子数组内数字个数;小于0,权值为数字个数的相反数;若等于0,权值为0.现对原序列进行划分,求所有子数组权值之和的最大值。

简易题解:观察到只有区间内数字之和大于0,才能产生贡献,考虑进行动态规划。

f[i] 表示前i位能得到的权值最大值。

求出原序列的前缀和数组p,朴素dp式如下:

for(int i = 1; i <= n; i ++) {
			for(int j=0; j<i; j++) 
				if(p[i] - p[j] > 0) f[i] = max(f[i], f[j] + i - j);
				else if(p[i] - p[j] == 0) f[i] = max(f[i], f[j]);
				f[i] = max(f[i], f[i - 1] - 1);
		}

可以发现,对于每个i,只需要找到所有 满足 p[j] < p[i]jf[j] + j 的最大值。

相当于维护前缀上的最大值,所有可以用树状数组来做。由于p[i] 的范围是1e9级别,树状数组无法开这么大,但 i 的数量只有5e5,所以可以进行离散化后在进行操作。

t2数组用于维护与当前前缀相等的f[i] 的最大值,因为只涉及最值单点修改和单点查询,所以直接用一个普通数组维护即可。

/*
	朴素dp式:
		for(int i = 1; i <= n; i ++) {
			for(int j=0; j<i; j++) 
				if(p[i] - p[j] > 0) f[i] = max(f[i], f[j] + i - j);
				else if(p[i] - p[j] == 0) f[i] = max(f[i], f[j]);
				f[i] = max(f[i], f[i - 1] - 1);
		}
*/

#include <bits/stdc++.h>

using namespace std;

const int N  = 5e5 + 10;
#define lowbit(x) (x & -x)
#define int long long

int n;
map<int, int> id;

struct BIT {
	int t[N];
	inline void modify(int x, int k) {
		while(x <= n) {
			t[x] = max(t[x], k);
			x += lowbit(x);
		}
	}
	inline int query(int x) {
		int res = -2e14;
		while(x) {
			res = max(t[x], res);
			x -= lowbit(x);
		}
		return res;
	}
	inline clear() {
		for(int i=0; i<=n; i++) 
			t[i] = -2e14;
	}
} t1; //t1维护f[j] - j的最值

void slv() {

	cin >> n;
	t1.clear();
	vector<int> f(n + 1, -2e14), p(n + 1, 0), t2(n + 10, -2e14), st(n + 10, 0);
	for(int i = 1; i <= n; i ++) {
		int x; cin >> x;
		p[i] = p[i - 1] + x;
	}
	auto v = p;
	sort(v.begin(), v.end());
	for(int i=0; i<=n; i++) {
		if(i == 0) {
			p[i] = lower_bound(v.begin(), v.end(), p[i]) - v.begin() + 1;
			f[0] = 0;
			t2[p[i]] = max(0LL, t2[p[i]]);
			t1.modify(p[i], 0);
		}
		else p[i] = lower_bound(v.begin(), v.end(), p[i]) - v.begin() + 1;
	}
	//for(int i=0; i<=n; i++) cout << p[i] << ' ';
	
	for(int i = 1; i <= n; i ++) {
		int ID = p[i];
		f[i] = max(f[i], t1.query(ID - 1) + i);
		f[i] = max(f[i], t2[ID]);
		f[i] = max(f[i - 1] - 1, f[i]);
		t1.modify(ID, f[i] - i);
		t2[ID] = max(t2[ID], f[i]);
	}
	cout << f[n] << '\n';
}

signed main() {
	cin.tie(nullptr)->sync_with_stdio(false);
	int _; cin >> _;
	while(_--) 
		slv();
}


     

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值