【bzoj1044】木棍分割
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,10
00),1<=Li<=1000.
Output
输出有2个数, 第一个数是总长度最大的一段的长度最小值, 第二个数是有多少种砍的方法使得满足条件.
Sample Input
3 2
1
1
10
1
1
10
Sample Output
10 2
Solution
第一问我们使用二分查找查询每个区间的长度上线是多少
对于第二问,我们使用动态规划
f[i][j]=simga(f[k][j-1])(s[i]-s[k]<=lim)
显然的我们可以使用前缀和来解决问题
然后对于每个i,k最小能取到多少是确定的,我们预处理一下,
然后加个滚动数组就一遍a了,开心!
#include<stdio.h> #include<stdlib.h> #include<iostream> #include<string> #include<string.h> #include<algorithm> #include<math.h> #include<queue> #include<map> #include<vector> #include<set> #define il inline #define re register #define mod 10007 using namespace std; const int N=51111; int n,m,a[N],l=0,r=0,mid,s[N],lim,p[N],f[2][N],lst,now,ans; il bool chk(int lim){ int cnt=0,tot=1; for(int i=1;i<=n;i++){ if(cnt+a[i]<=lim){ cnt+=a[i]; } else{ cnt=a[i];tot++; } } return tot<=m; } int main(){ scanf("%d%d",&n,&m);m++; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); r+=a[i];l=max(l,a[i]); } while(l<r){ mid=(l+r)/2; if(chk(mid)) r=mid; else l=mid+1; } lim=r; for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i]; for(int i=1;i<=n;i++){ l=1;r=n; while(l<r){ mid=(l+r)/2; if(s[i]-s[mid-1]<=lim) r=mid; else l=mid+1; } p[i]=r; } lst=0;now=1; for(int i=1;i<=n;i++) if(s[i]<=lim) f[lst][i]=f[lst][i-1]+1; else f[lst][i]=f[lst][i-1]; ans=(f[0][n]-f[0][n-1])%mod; for(int j=2;j<=m;j++){ memset(f[now],false,sizeof(f[now])); for(int i=j,x;i<=n;i++){ x=(f[lst][i-1]-f[lst][max(p[i]-2,0)])%mod; f[now][i]=(f[now][i-1]+x)%mod; } ans=(ans+f[now][n]-f[now][n-1]+mod)%mod; swap(now,lst); } cout<<lim<<" "<<ans; return 0; }
蜉蝣渴望着飞翔,尽管黄昏将至