版权声明: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;
}