斜堆与左偏树

2809的dipatching是一道很SB的题啊。
实际上就是在以某一个节点x为根的子树中选一些点,使得sigma(salary.i)<=m的前提下lead.x*num最大(num为选的点个数)。
题意很好理解,思路也很好想,维护一个类似堆的东西,dfs时要支持合并,超过m之后要删除最大元素。
无聊就用了两种方法写,左偏树和斜堆,算作练习。

斜堆

总结起来斜堆是个很SB的东西,什么都不用维护直接最后swap左右儿子。均摊log
/**************************************************************
    Problem: 2809
    User: liuxin
    Language: Pascal
    Result: Accepted
    Time:2060 ms
    Memory:5516 kb
****************************************************************/

type
    Edge=record
        ed,nt:array[0..200005]of longint;
        lt:array[0..100005]of longint;
        l:longint;
    end;
    Node=record l,r,v:longint; end;
var
    E:Edge;
    ans:int64;
    i,k,n,m,cnt:longint;
    h:array[0..100005]of Node;
    heap,size,lead,cap,num:array[0..100005]of longint;

    procedure swap(var i,j:longint);var k:longint; begin k:=i; i:=j; j:=k; end;
    function merge(p,q:longint):longint;
    begin
        if (p=0) then exit(q) else if (q=0) then exit(p);
        if (h[p].v<h[q].v) then swap(p,q);
        h[p].r:=merge(h[p].r,q); swap(h[p].l,h[p].r); exit(p);
    end;
    procedure dfs(x:longint);
    var i,y:longint;
    begin
        cap[x]:=size[x]; num[x]:=1; i:=E.lt[x];
        inc(cnt); heap[x]:=cnt; h[cnt].l:=0; h[cnt].r:=0; h[cnt].v:=size[x];
        while (i>0) do begin
            y:=E.ed[i]; dfs(y);
            cap[x]:=cap[x]+cap[y]; num[x]:=num[x]+num[y];
            heap[x]:=merge(heap[x],heap[y]);
            while (cap[x]>m) do begin
                cap[x]:=cap[x]-h[heap[x]].v; dec(num[x]);
                heap[x]:=merge(h[heap[x]].l,h[heap[x]].r);
            end;
            i:=E.nt[i];
        end;
        if (int64(num[x])*lead[x]>ans) then ans:=int64(num[x])*lead[x];
    end;
    procedure add(x,y:longint);
    begin inc(E.l); E.ed[E.l]:=y; E.nt[E.l]:=E.lt[x]; E.lt[x]:=E.l; end;

begin
    readln(n,m); E.l:=0; ans:=0;
    for i:=1 to n do begin
        readln(k,size[i],lead[i]);
        if (k>0) then add(k,i);
    end;
    dfs(1); writeln(ans);
end.

左偏树

左偏树看起来科学一点,有严格的证明,多维护了一个东西感觉不错。严格log
/**************************************************************
    Problem: 2809
    User: liuxin
    Language: Pascal
    Result: Accepted
    Time:2364 ms
    Memory:8324 kb
****************************************************************/

type
    Node=^HeapNode;
    HeapNode=record
        key,dis:int64;
        l,r:Node;
    end;
    Edge=record
        l:longint;
        ed,nt,lt:array[0..100005]of longint;
    end;
var
    E:Edge;
    Null:Node;
    ans:int64;
    i,k,n,m,root:longint;
    H:array[0..100005]of Node;
    salary,lead,num,sum:array[0..100005]of int64;

    function Newnode(k:longint):Node; var tmp:Node;
    begin new(tmp); tmp^.l:=Null; tmp^.r:=Null; tmp^.key:=k; tmp^.dis:=1; exit(tmp); end;
    function Merge(x,y:Node):Node;
    var tmp:Node;
    begin
        if (x=Null) then exit(y);
        if (y=Null) then exit(x);
        if (x^.key<y^.key) then begin tmp:=x; x:=y; y:=tmp; end;
        x^.r:=Merge(x^.r,y);
        if (x^.r^.dis>x^.l^.dis) then begin tmp:=x^.r; x^.r:=x^.l; x^.l:=tmp; end;
        x^.dis:=x^.r^.dis+1; exit(x);
    end;
    procedure dfs(t:longint);
    var i,j:longint;
    begin
        num[t]:=1; sum[t]:=salary[t]; H[t]:=Newnode(salary[t]); i:=E.lt[t];
        while (i>0) do begin
            j:=E.ed[i]; dfs(j); 
            num[t]:=num[t]+num[j]; sum[t]:=sum[t]+sum[j]; H[t]:=Merge(H[t],H[j]); 
            i:=E.nt[i];
        end;
        while (sum[t]>m) do
            begin dec(num[t]); dec(sum[t],H[t]^.key); H[t]:=Merge(H[t]^.l,H[t]^.r); end;
        if (ans<num[t]*lead[t]) then ans:=num[t]*lead[t];
    end;
    procedure add(x,y:longint);
    begin inc(E.l); E.ed[E.l]:=y; E.nt[E.l]:=E.lt[x]; E.lt[x]:=E.l; end;

begin
    readln(n,m);
    fillchar(E.lt,sizeof(E.lt),0); E.l:=0;
    for i:=1 to n do begin
        readln(k,salary[i],lead[i]); 
        if (k=0) then root:=i else add(k,i);
    end;
    new(Null); Null^.key:=0; Null^.dis:=0; Null^.l:=Null; Null^.r:=Null;
    ans:=0; dfs(root); writeln(ans);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值