Helvetic Coding Contest 2018 online mirror C3. Encryption (hard) 樹狀陣列+DP
阿新 • • 發佈:2018-12-13
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; }