1. 程式人生 > >Helvetic Coding Contest 2018 online mirror C3. Encryption (hard) 樹狀陣列+DP

Helvetic Coding Contest 2018 online mirror C3. Encryption (hard) 樹狀陣列+DP

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