bzoj 1044 贪心二分+DP

原题传送门http://www.lydsy.com/JudgeOnline/problem.php?id=1044

首先对于第一问,我们可以轻易的用二分答案来搞定,对于每一个二分到的mid值

我们从len[i]开始累加,每到累加值>mid的时候,就累加一个需要砍的次数,然后

比较次数和m的大小关系,然后二分就行了,这里有个小贪心,对于一个len[i],我们

尽量的不让他消耗一次砍得次数,直到非砍不可了才砍。

那么问题就转化成了我们有N个木条的长度,用最多M刀将他们分为不超过ans长度的方案数

我们用w[j,i]代表砍j刀,前i个木条的方案数,那么可以轻易的得到转移方程

w[j,i]:=sigma(w[j-1,k]) sum[i]-sum[k-1]<=ans

其中sum是长度的前缀和

分析下,这个时间复杂度是n*n*m的,明显过不去,那么想下优化

我们可以知道,sum是不变的,换句话说,就是每个转移到I的k是不变的,且是连续区间

那么我们对于每个i,存下pre[i],代表最早pre[i]能更新I,那么我们w[j,i]也存成前缀和,

对于每个w[j,i]就可以o(1)的转移了

而且这道题会卡空间,需要用滚动数组

 

自己的超时了,照着大神的改了改。。。

/**************************************************************
    Problem: 1044
    User: BLADEVIL
    Language: Pascal
    Result: Accepted
    Time:2040 ms
    Memory:1208 kb
****************************************************************/
 
var
    w                           :array[0..1,-3..50000] of longint;
    pre, a, s                   :array[-1..50000] of longint;
    last                        :array[-1..1001] of longint;
    p, ans2, tot                :longint;
    max, tmp                    :longint;
    i, j, h, k                  :longint;
    l, r, mid, ans              :longint;
    n, m                        :longint;
       
function check(x:longint):boolean;
var i                           :longint;
begin
    if max>x then exit(false);
    tmp:=0;
    tot:=0;
    for i:=1 to n do
    begin
        if tmp+a[i]<=x then tmp:=tmp+a[i]  
        else begin
            tmp:=a[i];
            inc(tot);
            if tot>m then exit(false);    
        end;  
    end;
    exit(true);
end;
   
begin
    readln(n,m);
    if n=0 then
    begin
        writeln(0,' ',1);
        exit;  
    end;
    for i:=1 to n do
    begin
        readln(a[i]);
        s[i]:=s[i-1]+a[i];  
        if max<a[i] then max:=a[i];
    end;
    l:=1;
    r:=s[n];
    while l<r do
    begin
        if l=r-1 then
        begin
            if check(l) then ans:=l
                else ans:=r;
            break;
        end;
        mid:=(l+r) shr 1;
        if check(mid) then r:=mid else l:=mid;
    end;
        
    tmp:=0;
    tot:=0;
    for i:=1 to n do
    begin
        if tmp+a[i]>ans then
        begin
            tmp:=a[i];  
            inc(tot); 
            last[tot]:=i-1; 
        end else tmp:=tmp+a[i];         
    end;
    for i:=tot+1 to m+1 do last[i]:=n;
    h:=1;
    for i:=1 to n do
    begin
        while s[i]-s[h]>ans do inc(h);
        pre[i]:=h;       
    end; 
        
    for i:=1 to last[1] do w[1,i]:=w[1,i-1]+1;
    for i:=last[1]+1 to last[2] do w[1,i]:=w[1,i-1];
    l:=1;
    for i:=2 to m+1 do
    begin
        k:=l;
        l:=k xor 1;
        w[l,i]:=1;
        p:=1;
        for j:=i+1 to last[i] do
        begin
            p:=w[k,j-1]-w[k,pre[j]-1];
            w[l,j]:=(w[l,j-1]+p) mod 10007;
        end;
        if last[i]=n then ans2:=(ans2+p) mod 10007
        else begin
            for j:=last[i]+1 to last[i+1] do w[l,j]:=w[l,j-1];
        end; 
    end;
    writeln(ans,' ',(ans2 mod 10007+10007) mod 10007);
end.

 

posted on 2013-11-21 17:21  BLADEVIL  阅读(362)  评论(0编辑  收藏  举报