POJ3245 神題 二分+DP+單調隊列+堆/線段樹/平衡樹

未做POJ3017Cut the Sequence者請先忽略此文

首先膜拜杜神犇:http://hi.baidu.com/billdu/blog/item/215a8defcbc8a43c2df53498.html

若有不懂再看本文

題目大意:給定長度爲n的序列對A,B,將序列對分爲若干連續子段使其滿足以下性質

1:任意兩對元素(Ap,Bp)和(Aq,Bq)若p,q不在同一段中且有p<q  則Bp>Aq

2:定義Mi爲第i段包含的元素中A的最大值,即Mi = max{AliAli+1, ..., Ari}, 1 ≤ i ≤ p

li ri爲第i段的最左端和最右端 p爲段數。

使得sigma{Mi}<=lima,lima爲一個給定的值。

Task:

定義Si爲第i段的B值的和。

滿足上述條件下使得max{Si:1 ≤ i ≤ p}最小

思路:

其實預處理的思路並不是我的,來源於杜神犇,剩下的二分大家基本上都可以想出來,DP其實和POJ3017是一樣的,我是先做了3017的。

這題難在滿足第一個條件上,如果正向思考是不可以的(或者說很難),那麼逆向思考。

只要是p<q且Bp<=Aq的必須在一起,

引用杜神犇的話:

對於第一個條件,我們不難發現,對於任意的p < q,如果Bp <= Aq,那麼從p到q的這一段都必須在一個區間裏。我們可以抽象爲一張N個節點的圖,如果[p, q]必須在一個區間裏,那麼連邊(p, p + 1), (p + 1, p + 2), ..., (q - 1, q)。找到所有這樣的關係,我們可以發現,所有的連通塊都必須屬於一個區間,所以說對於每一個連通塊,我們將其縮成一個數對,它的A值爲連通塊中所有數對的A的最大值,B爲連通塊中所有數對的B的和。經過這樣的過程,我們在以後的工作中就不用考慮第一個條件了。

但裸做是不可以的。

由於子段是連續的,那麼對於任意一個p,只有最大的q滿足Bp<=Aq纔有用

那麼只需在掃描到Bp之前(或之時)Bp<=Aq的所有q都掃描到就可以了。

那麼就分別對A,B降序排序,記錄A,B原位置,由於A,B排好序了,只要掃描到Bp時,將所有的Bp<=Aq的Aq掃到就可以了。

那麼算法流程就出來了:

step1:Sort A,Sort B(以下i,j表示sort之後的下標,pos指sort之前的下標)

step2:若當前B[i]>A[j]則表示不存在B[i]<=A[j],last[i]=i(其實就是nil或0),否則移動j指針直到A[j]<B[i]且記錄一個maxlast表示A[1]-A[j-1]的最大的A的pos,last[i]=maxlast,j賦爲j-1

step3:處理以上之後最後掃一遍,記錄一個新的A和B A值爲連通塊中所有數對的原A的最大值 B爲連通塊中所有數對的原B的和

預處理就是O(nlogn)

這樣就只剩下二分和Dp了

這樣就和POJ3017一樣了

f[i]爲前i個元素分段後最小的S值

f[i]=min(f[j]+max(A[j+1]....A[i]))條件sigma{B[j+1]+..+B[i]}<=二分的Ans

當f[n]<=lima時Ans可行否則不可行

用單調隊列和堆維護

由於數據水,只用單調隊列也可以過數據。

Code:未用堆 72MS

program poj3245;

type    som=record
          data,pos:longint;
        end;

        xom=record
          ta,tb:longint;
        end;

var     f,a,b,pos,s,last:array[0..50001]of longint;

        op1,op2:array[0..50001]of som;

        temp:array[0..50001]of xom;

        p,n,i,j,lc,rc,lima,head,tail,lasta,sumb,num,mid,top:longint;

        tmps:som;

function min(q,p:longint):longint;
begin   if q<p then exit(q) else exit(p);
end;
function max(q,p:longint):longint;
begin   if q<p then exit(p) else exit(q);
end;

function dp(limb:longint):boolean;
begin   fillchar(f,sizeof(f),0);
        fillchar(pos,sizeof(pos),0);
        p:=0;
        head:=1;tail:=0;
        for i:=1 to n do
          begin
            while s[i]-s[p]>limb do inc(p);
            while (a[i]>a[pos[tail]])and(head<=tail) do dec(tail);
            inc(tail);
            pos[tail]:=i;
            while (s[i]-s[pos[head]-1]>limb)and(head<=tail) do inc(head);
            f[i]:=f[p]+a[pos[head]];
            for j:=head to tail-1 do
              f[i]:=min(f[i],f[pos[j]]+a[pos[j+1]]);
          end;
        if f[n]>lima then exit(false)
                     else exit(true);
end;

procedure qs(h,g:longint);
var     l,k,mid:Longint;
begin   l:=h;k:=g;mid:=op1[(l+k)div 2].data;
        repeat
          while op1[l].data>mid do inc(l);
          while op1[k].data<mid do dec(k);
          if l<=k
            then
              begin
                tmps:=op1[l];
                op1[l]:=op1[k];
                op1[k]:=tmps;
                inc(l);dec(k);
              end;
        until l>k;
        if l<g then qs(l,g);
        if h<k then qs(h,k);
end;
procedure sort(h,g:longint);
var     l,k,mid:Longint;
begin   l:=h;k:=g;mid:=op2[(l+k)div 2].data;
        repeat
          while op2[l].data>mid do inc(l);
          while op2[k].data<mid do dec(k);
          if l<=k
            then
              begin
                tmps:=op2[l];
                op2[l]:=op2[k];
                op2[k]:=tmps;
                inc(l);dec(k);
              end;
        until l>k;
        if l<g then sort(l,g);
        if h<k then sort(h,k);
end;

begin   
        readln(n,lima);
        for i:=1 to n do
          begin
            readln(op1[i].data,op2[i].data);
            temp[i].ta:=op1[i].data;
            temp[i].tb:=op2[i].data;
            sumb:=sumb+op2[i].data;
            op1[i].pos:=i;
            op2[i].pos:=i;
          end;
        qs(1,n);
        sort(1,n);
        p:=1;
        for i:=1 to n do
          begin
            if op1[p].data<op2[i].data
              then
                continue;
            while (op1[p].data>=op2[i].data)and(p<=n) do
              begin
                lasta:=max(op1[p].pos,lasta);
                inc(p);
              end;
            dec(p);
            last[op2[i].pos]:=lasta;
          end;
        i:=1;
        while i<=n do
          begin
            inc(num);
            a[num]:=temp[i].ta;
            b[num]:=temp[i].tb;
            j:=i;
            top:=last[i];
            while j<top do
              begin
                inc(j);
                top:=max(last[j],top);
                a[num]:=max(a[num],temp[j].ta);
                b[num]:=b[num]+temp[j].tb;
              end;
            i:=max(top,i)+1;
          end;
        lc:=0;
        for i:=1 to n do lc:=max(lc,b[i]-1);
        rc:=sumb;
        n:=num;
        for i:=1 to n do
          s[i]:=s[i-1]+b[i];
        while lc<rc-1 do
          begin
            mid:=(lc+rc)div 2;
            if dp(mid)
              then
                rc:=mid
              else
                lc:=mid;
          end;
        writeln(rc);
end.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章