# BZOJ 1044: [HAOI2008]木棍分割 DP 前缀和优化

#### 状态转移方程就是$$f[k][i]=f[k][i]+f[k-1]j$$

for(int i=1;i<=n;++i) {
if(sum[i]<=ans)
f[1][i]=1;
}
for (int k=2;k<=m+1;++k) {
for(int i=k;i<=n;++i) {
for(int j=i;j>=1;--j) {
if(sum[i]-sum[j-1] <= ans) {
f[k][i] += f[k-1][j];
}
}
}
}


### 最后，注意边界吧

/**************************************************************
Problem: 1044
User: 3010651817
Language: C++
Result: Accepted
Time:4528 ms
Memory:11052 kb
****************************************************************/

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 5e5 + 7;
const int mod = 10007;
int a[maxn], n, m, l, r;
int sum[maxn], p[maxn];
bool check(int x) {
int js = 0, tot = 0;
for (int i = 1; i <= n; ++i) {
if (tot + a[i] > x) {
js++, tot = 0;
}
tot += a[i];
}
if (tot > x) return 0;
return m >= js;
}
int f[2][maxn];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i];
r = sum[n];
int ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
p[1] = 1;
for (int i = 2; i <= n; ++i) {
p[i] = p[i - 1];
while (sum[i] - sum[p[i] - 1] > ans && p[i] <= i) {
p[i]++;
}
}
for (int i = 1; i <= n; ++i) p[i] = p[i] >= 2 ? p[i] - 2 : 0;
for (int i = 1; i <= n; ++i) {
if (sum[i] <= ans) {
f[1][i] = 1;
}
f[1][i] = f[1][i - 1] + f[1][i];
}
int tot = 0;
for (int i = 2, cnt = 0; i <= m + 1; ++i, cnt ^= 1) {
for (int j = i; j <= n; ++j) {
f[cnt][j] = ((f[cnt][j - 1] + f[cnt ^ 1][j - 1]) % mod + mod - f[cnt ^ 1][p[j]]) % mod;
}
tot = ((tot + f[cnt][n]) % mod + mod - f[cnt][n - 1]) % mod;
}

printf("%d %d\n", ans, tot );
return 0;
}

posted @ 2018-10-09 19:15  复杂的哈皮狗  阅读(128)  评论(0编辑  收藏