dp+二分
第一问二分一个分割后的最大值看能不能满足就行
第二问用 f[i][j]表示前i个切成j段有多少种 f[i][j]=∑f[i-1][k] s[k+1]+s[k+2]+...+s[i]<=ans1,能不拆开的部分一定连续,并且随着i增大而后移然后就可以用单调队列维护一下
代码
//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=50050;
const int mod=10007;
int n,m,le[M];
int s[M],f[2][M];
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline bool che(int x)
{
int cut=0,lon=0;
for (int i=1;i<=n;i++)
{
lon+=le[i];
if (lon>x) cut++,lon=le[i];
if (cut>m||le[i]>x) return 0;
}
if (lon>0) cut++;
return cut<=m;
}
int main()
{
n=read();m=read()+1;
for(int i=1;i<=n;i++) le[i]=read(),s[i]=s[i-1]+le[i];
int l=1,r=s[n],ans;
while(l<=r)
{
int mid=(l+r)>>1;
if(che(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
f[0][0]=f[1][0]=1;
int pre=0,now=1;
for(int j=1;j<=m;j++,pre=now,now^=1)
{
for(int i=1,tot=0,l=1,r=0;i<=n;i++)
{
tot=(tot+f[pre][r++])%mod;
while(l<=r&&s[r]-s[l-1]>ans)tot=(tot+mod-f[pre][l-1])%mod,++l;
f[now][i]=tot;
}
}
printf("%d %d",ans,f[pre][n]);
return 0;
}