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

版权声明:xgc原创文章,未经允许不得转载。 https://blog.csdn.net/xgc_woker/article/details/82932212

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;
}

猜你喜欢

转载自blog.csdn.net/xgc_woker/article/details/82932212