bzoj1044

好题

第一问不难,毕竟二分答案类的题目在USACO上都练了好多遍了

第二问充分的暴露了我dp渣的本性

一开始楞是没想出来

f[i,j]表示到第i根木棒切了j刀满足最长段小于等于ans的方案数

式子是这样的f[i,j]=sigma(f[k,j-1]) if sum[i]-sum[k]<=ans

然后发现我的优化水平还是不错的,

首先是空间上的问题,观察得知,切这刀的方案数只与切前一刀有关,于是我们滚动数组

再看时间,观察k的选取,与这是第几刀无关

再看,如果满足sum[i]-sum[k]<=ans 那么k~i-1一定都是符合的切法

于是我们预先处理一下即可

复杂度为O(nlogn+mn)

 1 const mo=10007;
 2 var f:array[0..1,0..50010] of longint;
 3     sum,s,a,b:array[0..50010] of longint;
 4     p,l,r,mid,n,m,i,j,t,ans:longint;
 5 
 6 function check(s:longint):boolean;
 7   var t,len,i:longint;
 8   begin
 9     t:=0;
10     len:=0;
11     p:=0;
12     for i:=1 to n do
13       if len+a[i]>s then
14       begin
15         inc(t);
16         if t>m then exit(false);
17         if len>p then p:=len;
18         len:=a[i];
19       end
20       else len:=len+a[i];
21     if len>p then p:=len;
22     exit(true);
23   end;
24 begin
25   readln(n,m);
26   for i:=1 to n do
27   begin
28     readln(a[i]);
29     sum[i]:=sum[i-1]+a[i];
30     if l<a[i] then l:=a[i];
31   end;
32   r:=sum[i];
33   while l<=r do     //二分答案
34   begin
35     mid:=(l+r) shr 1;
36     if check(mid) then
37     begin
38       ans:=p;
39       r:=p-1;
40     end
41     else l:=mid+1;
42   end;
43   for i:=1 to n do
44     if sum[i]<=ans then f[0,i]:=1 else break;
45   j:=0;
46   for i:=1 to n do  //预处理
47   begin
48     while sum[i]-sum[j]>ans do inc(j);
49     b[i]:=j;
50   end;
51   p:=0;
52   t:=f[p,n];
53   for i:=1 to m do
54   begin
55     p:=1-p;
56     fillchar(s,sizeof(s),0);
57     for j:=1 to n do
58     begin
59       if b[j]-1>0 then r:=b[j]-1 else r:=0; //小细节
60       f[p,j]:=(s[j-1]-s[r]+mo) mod mo;
61       s[j]:=(s[j-1]+f[1-p,j]) mod mo;
62     end;
63     t:=(t+f[p,n]) mod mo;
64   end;
65   writeln(ans,' ',t);
66 end.
View Code
原文地址:https://www.cnblogs.com/phile/p/4473237.html