好题
第一问不难,毕竟二分答案类的题目在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.