题面在这里
做法:
第一问直接二分答案。
第二问DP,我们定义
f[i][j]
表示前i根木棍分为j块的方案数。
首先要
O(n)
预处理出
nxt[i]
表示满足
a[i]−a[k−1]<=res
的最小的k。注意这里的
a[]
是题目中数据的前缀和。然后转移方程是这样:
f[i][1]=(a[i]<=res),f[i][j]=∑k=nxt[i]−1i−1f[k][j−1]
答案
ans=∑i=1m+1f[n][i]
考虑优化。可以滚动掉第二维,然后用前缀和优化即可。
/*************************************************************
Problem: bzoj 1044 [HAOI2008]木棍分割
User: fengyuan
Language: C++
Result: Accepted
Time: 3624 ms
Memory: 2068 kb
Submit_Time: 2017-12-14 21:44:28
*************************************************************/
#include<bits/stdc++.h>
#define rep(i, x, y) for (int i = (x); i <= (y); i ++)
#define down(i, x, y) for (int i = (x); i >= (y); i --)
#define mid ((l+r)/2)
#define lc (o<<1)
#define rc (o<<1|1)
#define pb push_back
#define mp make_pair
#define PII pair<int, int>
#define F first
#define S second
#define B begin()
#define E end()
using namespace std;
typedef long long LL;
//head
const int N = 50010;
const int MOD = 10007;
int n, m;
int a[N], f[N], g[N], nxt[N];
inline bool check(int x)
{
int st = 1, sum = 1;
rep(i, 1, n) if (a[i]-a[st-1] > x) sum ++, st = i;
return sum <= m;
}
int main()
{
scanf("%d%d", &n, &m); m ++; int l = 0, r;
rep(i, 1, n){
int x; scanf("%d", &x);
a[i] = a[i-1] + x;
l = max(l, x);
} r = a[n];
while (l <= r) if (check(mid)) r = mid-1; else l = mid+1;
//f[i][j] = 前i根木棍分为j块的方案数
int mi = r+1; printf("%d ", mi);
int j = 1;
rep(i, 1, n){
while (j < i && a[i]-a[j-1] > mi) j ++;
nxt[i] = j;
}
rep(i, 1, n) f[i] = f[i-1]+(a[i] <= mi);
int ans = a[n] <= mi;
rep(j, 2, m){
g[j-1] = 0;
rep(i, j, n){
g[i] = (f[i-1]-(nxt[i] == 1 ? 0 : f[nxt[i]-2])+MOD)%MOD;
if (i == n) ans = (ans+g[i])%MOD;
g[i] = (g[i]+g[i-1])%MOD;
}
rep(i, 1, n) f[i] = g[i];
}
printf("%d\n", ans);
return 0;
}