Description
有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果mod 10007。。。
Input
输入文件第一行有2个数n,m. 接下来n行每行一个正整数Li,表示第i根木棍的长度.
Output
输出有2个数, 第一个数是总长度最大的一段的长度最小值, 第二个数是有多少种砍的方法使得满足条件.
Sample Input
3 2
1
1
10
1
1
10
Sample Output
10 2
HINT
两种砍的方法: (1)(1)(10)和(1 1)(10)
数据范围
n<=50000, 0<=m<=min(n-1,1000).
1<=Li<=1000.
Source
本题第一问明显可以二分答案(满足可二分性)——二分答案ans贪心检测。
至于第二问,可以用序列dp来解决。f[i][j]表示前i段木棍,分成j份的合法方案数。转移也很好想,f[i][j]=Σf[k][j-1](pre[i]-pre[k]<=ans),pre代表前缀和。看上去复杂度是O(m*n2)。但其实可以优化一下,因为i↑,所有k的下界也↑。所以我们可以利用单调队列,记录对于f[i][j]所有合法的f[k][j-1]的和,以此进行dp。复杂度O(m*n)。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<cstdlib> 3 using namespace std; 4 5 #define rhl 10007 6 #define maxn 50010 7 #define maxm 1010 8 9 int n,m,ans,L[maxn],sum[maxn]; 10 short f[maxn][maxm],res[maxn]; 11 12 struct node 13 { 14 int a[maxn],*h,*t; 15 node() {h = t = a;} 16 17 inline void push(int k) 18 { 19 *(++t) = k; 20 for (int i = 1;i <= m;++i) 21 (res[i] += f[k][i-1]) %= rhl; 22 } 23 24 inline void pop(int k) 25 { 26 while (sum[k]-sum[*(h+1)] > ans) 27 { 28 for (int i = 1;i <= m;++i) 29 (res[i] -= f[*(h+1)][i-1]) %= rhl; 30 ++h; 31 } 32 } 33 }team; 34 35 inline bool okay(int l) 36 { 37 int i,j,tot = 0; 38 for (i = 1;i <= n;) 39 { 40 if (tot > m + 1) return false; 41 for (j = i-1;j+1 <= n&&sum[j+1]-sum[i-1] <= l;++j); 42 if (j < i) return false; 43 ++tot; i = j+1; 44 } 45 return tot <= m + 1; 46 } 47 48 inline void dp() 49 { 50 ++m; 51 f[0][0] = 1; 52 team.push(0); 53 for (int i = 1;i <= n;++i) 54 { 55 team.pop(i); 56 for (int j = 1;j <= m;++j) f[i][j] = res[j]; 57 team.push(i); 58 } 59 ans = 0; 60 for (int i = 1;i <= m;++i) (ans += f[n][i])%=rhl; 61 ans = ans % rhl + rhl; 62 ans %= rhl; 63 } 64 65 int main() 66 { 67 scanf("%d %d",&n,&m); 68 for (int i = 1;i <= n;++i) scanf("%d",L+i),sum[i] = sum[i-1] + L[i]; 69 int l = 1,r = sum[n],mid; 70 while (l <= r) 71 { 72 mid = (l + r) >> 1; 73 if (!okay(mid)) l = mid + 1; 74 else r = mid - 1; 75 } 76 printf("%d ",ans = l); 77 dp(); 78 printf("%d",ans); 79 return 0; 80 }