[BZOJ1044][HAOI2008]木棍分割 二分+贪心+dp+前缀和优化
1044: [HAOI2008]木棍分割
Time Limit: 10 Sec Memory Limit: 162 MB Submit: 4112 Solved: 1577 [Submit][Status][Discuss]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
HINT
两种砍的方法: (1)(1)(10)和(1 1)(10)
先二分出答案,然后设f[i][j]表示第i个点切断,切断了j次的方案数,j可以滚动。
用s[i]求出a的前缀和,用p[i]表示第i个位置最左侧的可切割点,用add[i]表示f的前缀和。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #define mod 10007 8 using namespace std; 9 int n,m; 10 int a[50005]; 11 int sum; 12 bool check(int now) { 13 int t=0,b=0; 14 for(int i=1;i<=n;i++) { 15 if(a[i]>now) return 0; 16 if(t+a[i]>now) { 17 b++; 18 t=a[i]; 19 } 20 else t+=a[i]; 21 } 22 return b<=m; 23 } 24 int f[50005]; 25 int s[50005]; 26 int add[50005],p[50005]; 27 int main() { 28 scanf("%d%d",&n,&m); 29 for(int i=1;i<=n;i++){scanf("%d",&a[i]); sum+=a[i];s[i]=s[i-1]+a[i];} 30 int l=0,r=sum; 31 while(l<=r) { 32 int mid=(l+r)>>1; 33 if(check(mid)) r=mid-1; 34 else l=mid+1; 35 } 36 int ans1=r+1; 37 printf("%d ",ans1); 38 int ans2=0; 39 for(int i=1;i<=n;i++) if(s[i]<=ans1) add[i]=add[i-1]+1;else add[i]=add[i-1]; 40 if(s[n]<=ans1) ans2++; 41 for(int i=1;i<=n;i++) { 42 int can=s[i]-ans1; 43 int t=lower_bound(s+1,s+i,can)-s; 44 p[i]=t; 45 } 46 for(int j=1;j<=m;j++) { 47 for(int i=1;i<=n;i++) { 48 f[i]=(add[i-1]-add[max(0,p[i]-1)]+mod)%mod; 49 } 50 ans2+=f[n];ans2%=mod; 51 for(int i=1;i<=n;i++) add[i]=(add[i-1]+f[i])%mod; 52 } 53 printf("%d",ans2); 54 }
O(∩_∩)O~ (*^__^*) 嘻嘻…… O(∩_∩)O哈哈~