BZOJ1044: [HAOI2008]木棍分割

1044: [HAOI2008]木棍分割

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1580  Solved: 567
[Submit][Status]

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

Sample Output

10 2

两种砍的方法: (1)(1)(10)和(1 1)(10)
数据范围
n<=50000, 0<=m<=min(n-1,1000).
1<=Li<=1000.

HINT

 

Source

题解:

第一问二分+贪心可以得出

第二份朴素的话最坏情况下是O(mn^2)的,但考虑到每一个接点有pre[i],表示满足s[i]-s[j]<=ans的最大j,这样转移的时候

有f[i,j]=sigma(f[k,j-1])(pre[i]<=k<=i-1),考虑到pre[i]是单增的的,所以我们可以用单调队列来搞,优化到O(mn),不过也可以不用队列,

只用一个临时变量来记录当前时刻有用的节点的权值和

下面说一些题外话:

1.感觉此题很难写

2.写程序的时候,一要易懂,不容易出错,而才是简短,优美,不要一上来就想着可以怎么怎么减少代码,否则会把自己搞晕

代码:

 1 const maxn=50000+100;p=10007;
 2 var i,j,n,m,x,ans,cnt,l,r,mid,t,sum:longint;
 3     q,pre,len,s:array[0..maxn] of longint;
 4     f:array[0..maxn,0..1] of longint;
 5 procedure init;
 6  begin
 7   readln(n,m);
 8   for i:=1 to n do begin readln(len[i]);s[i]:=s[i-1]+len[i];end;
 9   s[n+1]:=maxlongint;
10  end;
11 function test(x:longint):boolean;
12  var i,j,sum:longint;
13  begin
14   sum:=0;j:=0;
15   for i:=1 to n do
16    if sum+len[i]<=x then inc(sum,len[i])
17    else begin inc(j);sum:=len[i];end;
18  exit(j<=m);
19  end;
20 procedure main;
21  begin
22   l:=0;r:=1000000000;
23   while l<r do
24    begin
25      mid:=(l+r)>>1;
26      if test(mid) then r:=mid else l:=mid+1;
27    end;
28   ans:=l;
29   j:=0;