思路
先考慮 \(\Theta(n^2k)\) 的暴力 DP。
定義 \(dp_{i,j}\) 表示在前 \(i\) 個數中選取 \(j\) 個的最小和,轉移顯然:
\[dp_{i,j} = \min_{1 \leq k < i}\{dp_{k,j - 1} + s_{k + 1,i} \bmod p\}
\]
注意到一個性質:\(dp_{i,j} \equiv s_i \pmod p\)。因為前者是前 \(i\) 項分為若干段的模 \(p\) 下的和,與後者顯然在模 \(p\) 意義下同餘。
對於 \(dp_{i,j}\) 的兩個轉移的位置 \(k_1,k_2\),計算出的結果分別是,\(dp_{k_1,j - 1} + s_{k_1 + 1,i} \bmod p\) 與 \(dp_{k_2,j - 1} + s_{k_2 + 1,i} \bmod p\)。並且有:
\[dp_{k_1,j - 1} + s_{k_1 + 1,i} \bmod p \equiv dp_{k_2,j - 1} + s_{k_2 + 1,i} \bmod p \pmod p
\]
假設 \(dp_{k_1,j - 1} < dp_{k_2,j - 1}\),由於 \(s_{k_1 + 1,i} \bmod p\) 與 \(s_{k_2 + 1,i} \bmod p\) 一定小於 \(p\),所以前者一定小於後者。
這樣,對於每一個 \(j\),記錄最小的 \(dp_{i,j}\) 即可。
Code
#include <bits/stdc++.h>
#define re register
#define int long long
using namespace std;
const int N = 5e5 + 10,M = 110;
int n,m,p;
int arr[N],s[N];
int dp[N][M],f[M];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
#define sum(l,r) ((s[r] - s[l - 1]) % p)
signed main(){
n = read(),m = read(),p = read();
for (re int i = 1;i <= n;i++) s[i] = s[i - 1] + (arr[i] = read());
for (re int i = 1;i <= n;i++){
for (re int j = 1;j <= min(i,m);j++) dp[i][j] = dp[f[j - 1]][j - 1] + sum(f[j - 1] + 1,i);
for (re int j = 1;j <= min(i,m);j++){
if (!f[j] || dp[f[j]][j] > dp[i][j]) f[j] = i;
}
}
printf("%lld",dp[n][m]);
return 0;
}