第一问很容易,二分答案,然后贪心判断即可。得出第一问答案maxl之后,用DP解第二问。一个很容易想的状态就是f[i][j]表示前i根木棍,分成j段的方案数。转移是f[i][j] = sum {f[k][j-1]},i’ <= k < i,i’满足它是最小的满足sum{a[i’] ~ a[i] <= maxl}的数。状态数O(nm)已经快要爆了,所以转移上必须O(1)做到。很容易知道,i’关于i是满足单调性的,所以可以乱搞一下,维护一个和,做到O(1)转移。
#include <cstdio>
#include <algorithm>
#include <cstring>
#define P 10007
using namespace std;
int n, m, maxl, ans, L[50005], sumf[50005], f[50005];
void get(int &x)
{
char c = getchar(); x = 0;
while(c < '0' || c > '9') c = getchar();
while(c <= '9' && c >= '0') x = x*10+c-48, c = getchar();
}
void put(int x)
{
char s[10]; int cnt = 0;
if(!x) putchar('0');
while(x) s[++cnt] = (x%10)+48, x /= 10;
while(cnt) putchar(s[cnt--]);
}
int main()
{
get(n); get(m);
for(int i = 1; i <= n; i++)
{
get(L[i]);
maxl = max(maxl, L[i]);
}
int l = maxl, r = maxl*n, mid, cnt, len;
while(l < r)
{
mid = l+r>>1;
int i = 1; cnt = len = -1;
while(i <= n && len)
{
len = 0, cnt++;
while(len+L[i] <= mid && i <= n) len += L[i++];
}
if(cnt <= m) r = mid;
else l = mid + 1;
}
put(l); putchar(' ');
f[1] = 1;
for(int i = 1; i <= m+1; i++)
{
int suma = 0, beg = i-1; if(!beg) beg = 1;
for(int j = 1; j <= n; j++) sumf[j] = (sumf[j-1]+f[j])%P;
for(int j = i; j <= n; j++)
{
suma += L[j];
while(suma > l) suma -= L[++beg];
f[j] = (sumf[j-1]-sumf[beg-1]+P)%P;
if(j == i) f[j] = 1;
}
ans = (ans+f[n])%P;
}
put(ans);
return 0;
}