[HAOI2008]木棍分割

题目描述

有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果mod 10007。。。

输入输出格式

输入格式:

输入文件第一行有2个数n,m. 接下来n行每行一个正整数Li,表示第i根木棍的长度.

输出格式:

输出有2个数, 第一个数是总长度最大的一段的长度最小值, 第二个数是有多少种砍的方法使得满足条件.

输入输出样例

输入样例#1: 复制
3 2                           
1 
1
10
输出样例#1: 复制
10 2

说明

两种砍的方法: (1)(1)(10)和(1 1)(10)

数据范围

n<=50000, 0<=m<=min(n-1,1000).

1<=Li<=1000.

第一问二分答案

第二问dp

$f[i][j]$表示前i个点,断j次

显然$f[i][j]=sum_k^{}f[k][j-1]$

k满足sum[i]-sum[k-1]<=ans

这个k随着i增大而增大,所以可以单调

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 typedef long long lol;
 8 int L[50001],ans;
 9 int f[50001],sum[50001],res;
10 int n,m,Mod=10007;
11 int check(lol mid)
12 {int i;
13   int now=0,cnt=0;
14   for (i=1;i<=n;i++)
15     {
16       if (L[i]-L[i-1]>mid) return m+1;
17       int j=i;
18       while (j<=n&&L[j]-L[i-1]<=mid) j++;
19       cnt++;
20       if (cnt-1>m) return m+1;
21       i=j-1;
22     }
23   return cnt-1;
24 }
25 int main()
26 {int i,j;
27   cin>>n>>m;
28   for (i=1;i<=n;i++)
29     {
30       scanf("%d",&L[i]);
31       L[i]+=L[i-1];
32     }
33   int l=1,r=L[n];
34   while (l<=r)
35     {
36       int mid=(l+r)>>1;
37       if (check(mid)<=m) ans=mid,r=mid-1;
38       else l=mid+1;
39     }
40   cout<<ans<<' ';
41   for (i=1;i<=n;i++)
42     if (L[i]<=ans) f[i]=1;
43   if (L[n]<=ans) res=1;
44   else res=0;
45   for (i=1;i<=m;i++)
46     {
47       for (j=1;j<=n;j++)
48     {
49       sum[j]=(f[j]+sum[j-1]);
50       if (sum[j]>=Mod) sum[j]-=Mod;
51       f[j]=0;
52     }
53       int l=1;
54       for (j=i+1;j<=n;j++)
55     {
56       while (l<=j&&L[j]-L[l-1]>ans) l++;
57       f[j]=(sum[j-1]-sum[l-2]+Mod);
58       if (f[j]>=Mod) f[j]-=Mod;
59     }
60       res=(res+f[n]);
61       if (res>=Mod) res-=Mod;
62     }
63   cout<<res;
64 }