[BZOJ 1044][HAOI2008]木棍分割(二分+Dp)
Description
有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连
接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长
度最大的一段长度最小. 并将结果mod 10007
Solution
二分答案求最小化的最大长度,然后Dp方案数
dp[i][j]表示前i个物品砍j刀保持最大长度不超过ans的方案数
dp[i][j]=∑dp[k][j-1](∑L[l]<=ans k+1<=l<=i)
发现时间空间都要优化
O(n^2m)肯定T了 但是可以知道k是单调递增的 于是用了一个dp[][j-1]的前缀和pre[]随便搞搞,当然也可以单调队列做
空间的话开滚动数组就好了
【然而我滚的时候忘记清零,WAWAWAQAQ,而且只WA一个点,拍了半天没拍出来】
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<queue> #define Min(a,b) (a<b?a:b) #define Max(a,b) (a>b?a:b) #define Mod 10007 using namespace std; int n,m,L[50005],sum[50005],pre[50005],dp[50005][2]; int Read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ x=x*10+c-'0';c=getchar(); } return x*f; } bool check(int x) { int len=0,cnt=0; for(int i=1;i<=n;i++) { if(len+L[i]>x)cnt++,len=0; len+=L[i]; } if(cnt>m)return false; return true; } int main() { n=Read(),m=Read(); int l=1,r,ans; for(int i=1;i<=n;i++) { L[i]=Read(); sum[i]=sum[i-1]+L[i]; l=Max(l,L[i]); } r=sum[n]; while(l<=r) { int mid=(l+r)>>1; if(check(mid))ans=mid,r=mid-1; else l=mid+1; } int cur=0,tot=0; for(int i=1;i<=n;i++) if(sum[i]<=ans)dp[i][0]=1; for(int j=1;j<=m;j++) { cur^=1; for(int i=1;i<=n;i++) pre[i]=pre[i-1]+dp[i][cur^1]; for(int i=1;i<=n;i++) dp[i][cur^1]=0; int k=1; for(int i=1;i<=n;i++) { while(sum[i]-sum[k]>ans&&k<i) k++; dp[i][cur]+=pre[i-1]-pre[k-1],dp[i][cur]%=Mod; } tot=(tot+dp[n][cur])%Mod; } printf("%d %d\n",ans,tot); return 0; }