[bzoj1044] [HAOI2008]木棍分割
Description
有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果mod 10007。。。
Input
输入文件第一行有2个数n,m.接下来n行每行一个正整数Li,表示第i根木棍的长度.n<=50000,0<=m<=min(n-1,1000),1<=Li<=1000.
Output
输出有2个数, 第一个数是总长度最大的一段的长度最小值, 第二个数是有多少种砍的方法使得满足条件.
Sample Input
3 2
1
1
10
Sample Output
10 2
Solution
第一问二分+贪心,第二问直接前缀和优化dp就好了,dp要滚动一维。
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
const int maxn = 2e5+10;
const int mod = 10007;
int n,m,a[maxn],q[maxn],f[2][maxn],ans,sum[maxn],r[maxn],w[maxn];
int check(int x) {
int res=0,tmp=0;
for(int i=1;i<=n;i++) {
res+=a[i];
if(a[i]>x) return 0;
if(res>x) res=a[i],tmp++;
}return tmp<=m;
}
void solve1() {
int l=0,R=sum[n],mid;
while(l<=R) if(check(mid=((l+R)>>1))) R=mid-1,ans=mid;else l=mid+1;
printf("%d ",ans);
}
void solve2() {
for(int i=1;i<=n;i++) if(sum[i]<=ans) f[0][i]=1;else break;
for(int i=1,lst=0;i<=n;i++) {while(sum[i]-sum[lst]>ans) lst++;r[i]=lst;}
int res=f[0][n];
for(int t=1;t<=m;t++) {
int i=t&1;memset(f[i],0,(n+4)*4);
for(int j=1;j<=n;j++) w[j]=(w[j-1]+f[i^1][j])%mod;
for(int j=1;j<=n;j++) f[i][j]=(w[j-1]-w[max(r[j]-1,0)])%mod;
res=(res+f[i][n])%mod;
}write((res+mod)%mod);
}
int main() {
read(n),read(m);
for(int i=1;i<=n;i++) read(a[i]),sum[i]=sum[i-1]+a[i];
solve1();solve2();
return 0;
}