题意:如题
思路:首先二分枚举最大的长度。
dp[i][j]表示前一个木棍,切了j刀符合条件的方案数。
dp[i][j]+=dp[k][j-1] (sum[i]-sum[k]<=ans)
这个转移可以用前缀和维护。
状态总共O(n*m),转移O(1)
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=b;i>=a;i--)
using namespace std;
#define ll long long
const int N=3e5+5;
const int mod = 998244353;
int n,k,pre,now;
int dp[1100];
short int sum[50010][1010];
int a[50101],ans,L[50100];
ll rd()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
bool check(int x)
{
int sum=0,cnt=0;
rep(i,1,n)
{
if(a[i]>x) return 0;
sum+=a[i];
if(sum>x)
{
sum=a[i];
cnt++;
}
}
return cnt<=k?1:0;
}
void init()
{
n=rd();k=rd();
rep(i,1,n) a[i]=rd();
int l=0,r=1e9;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%d ",ans);
int pre,sum=0;
pre=1;
rep(i,1,n)
{
sum+=a[i];
if(sum>ans)
{
while(sum>ans) sum-=a[pre++];
}
L[i]=pre;
//printf(" %d\n",L[i]);
}
}
void work()
{
dp[0]=1;
rep(i,2,n)
{
rep(j,0,k)
{
sum[i-1][j]=(sum[i-2][j]+dp[j])%10007;
}
if(L[i]==1) dp[0]=1;
else dp[0]=0;
rep(j,1,k)
{
if(L[i]-2<0) dp[j]=sum[i-1][j-1];
else dp[j]=(sum[i-1][j-1]-sum[L[i]-2][j-1]+10007)%10007;
//printf("%d ",dp[j]);
}
//printf("\n");
}
int sum=0;
rep(i,0,k) sum=(sum+dp[i])%10007;
printf("%d\n",sum);
}
int main()
{
init();
work();
}