Helvetic Coding Contest 2018 online mirror C3. Encryption (hard) 树状数组+DP

本文介绍了一种利用动态规划(DP)解决序列分割问题的方法,并通过树状数组进行优化,以实现O(nk)的时间复杂度。具体探讨了如何通过树状数组加速DP过程,以及如何在给定序列中找到权值总和最小的k段分割。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
给你一个序列,要你分成k块,每一段的权值定义为这一段的和%P,求权值总和最小。


Sample Input
4 3 10
3 4 7 2


Sample Output
6


考虑O(nk)的DP,
设f[i][j]为i分成j段。
对于当前的s[i],就分两种情况继承,一是小于等于s[i],一是大于等于s[i]的,
这个你可以用树状数组加速。。。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
int _min(int x, int y) {return x < y ? x : y;}
int _max(int x, int y) {return x > y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

int P, s1[110], s2[110];

int lowbit(int x) {return x & -x;}
void change1(int x, int c) {for(int i = x; i <= P; i += lowbit(i)) s1[i] = _min(s1[i], c);}
void change2(int x, int c) {for(int i = x; i <= P; i += lowbit(i)) s2[i] = _min(s2[i], c);}
int getmin1(int x) {int minn = 999999999; for(int i = x; i; i -= lowbit(i)) minn = _min(minn, s1[i]); return minn;}
int getmin2(int x) {int minn = 999999999; for(int i = x; i; i -= lowbit(i)) minn = _min(minn, s2[i]); return minn;}

int s[510000], f[2][510000];

int main() {
	int n = read(), K = read(); P = read();
	for(int i = 1; i <= n; i++) s[i] = read(), s[i] += s[i - 1], s[i] %= P;
	memset(f[0], 63, sizeof(f[0])); int now = 0;
	f[0][0] = 0;
	for(int k = 1; k <= K; k++) {
		now ^= 1;
		memset(s1, 63, sizeof(s1));
		memset(s2, 63, sizeof(s2));
		change1(s[k - 1] + 1, f[now ^ 1][k - 1] - s[k - 1]);
		change2(P - s[k - 1], f[now ^ 1][k - 1] - s[k - 1]);
		for(int i = k; i <= n; i++) {
			f[now][i] = getmin1(s[i] + 1) + s[i];
			f[now][i] = _min(f[now][i], s[i] + P + getmin2(P - s[i]));
			change1(s[i] + 1, f[now ^ 1][i] - s[i]);
			change2(P - s[i], f[now ^ 1][i] - s[i]);
		}
	} printf("%d\n", f[now][n]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值