题目:
分割木棍
题目描述
有 n 根木棍,第 i 根木棍的长度为 Li,n 根木棍依次连结了一起,总共有 n-1 个连接处。现在允许你最多砍断 m 个连接处,砍完后 n 根木棍被分成了很多段,要求满足总长度最大的一段长度最小,并且输出有多少种砍的方法使得总长度最大的一段长度最小。并将结果 mod 10007。
输入格式
输入文件第一行有 2 个数 n,m。接下来 n 行每行一个正整数 Li,表示第 i 根木棍的长度。
输出格式
输出有 2 个数,第一个数是总长度最大的一段的长度最小值,第二个数是有多少种砍的方法使得满足条件。
输入样例
3 2
1
1
10
输出样例
10 2
提示
两种砍的方法:(1)(1)(10) 和 (1 1)(10)
数据范围
n≤50000,0≤m≤min(n-1,1000)
1≤Li≤1000
我的代码:
#include<bits/stdc++.h>
#define P 10007
#define mid (l+r>>1)
using namespace std;
const int N=5005;
int n,m,l,r,k,s,t,S[N],a[N],f[N][1005],sum[N][1005];
int check(int M){
int now=0,tot=1;
for(int i=1;i<=n;++i){
if(now+a[i]<=M)now+=a[i];
else {
now=a[i];
tot++;
}
}
if(tot<=m)return 1;
else return 0;
}
int main(){
cin>>n>>m;
m++;
for(int i=1;i<=n;++i){
cin>>a[i];
S[i]=S[i-1]+a[i];
l=max(l,a[i]);r+=a[i];
}
while(l<=r){
if(check(mid))r=mid-1;
else l=mid+1;
}
int ans=l,tmp=0;
f[0][0]=1;
for(int i=0;i<=n;++i) sum[i][0]=1;
int k=-1;
for(int i=1;i<=n;++i){
while(S[i]-S[k+1]>ans)
++k;
for(int j=1;j<=min(i,m);++j){
f[i][j]=(sum[i-1][j-1]-(k==-1?0:sum[k][j-1]))%P;
sum[i][j]=(sum[i-1][j]+f[i][j])%P;
}
}
for(int j=1;j<=m;++j) tmp=(tmp+f[n][j])%P;
cout<<ans<<' '<<(tmp+P)%P;
}