JZOJ 3987 【WC2015模拟2.6】Tree

Tree

Description

给出一棵有 N 个节点的树,从中选出K条不相交的路径,此时覆盖的点权和为 S ,则得分为SK+1,然后你必须 选择一个参数 C ,满足C[0,T],将所有点的点权加上 C 后对Lim取模,问最大得分为多少。

Data Constraint

N <=5000 T <Lim< 105 ,点权<= 105

Solution

首先,有一个显然的结论, C 的取值最多只有N种。

然后,显然答案具有二分性,所以我们可以考虑二分答案 Ans ,倘若算出来的答案比二分的答案要大,则有 Ans < SK+1 , 整理得 Ans < S -K* Ans
Ans < Ki=1 (SumiAns)
我们用 Sumi 表示第i条路径的点权和,用 Sumi - Ans 表示一条路径的权值

此时左边已经和式子的值没有关系了,考虑用树形动态规划求式子右边的最大值,
Gi Fi 分别表示以 i 为根的子树内路径权值和最大值 以及 必须有一条路径经过i可以延伸至 i 的祖先的路径时的路径权值和最大值,转移很显然。

但我们发现这样做的时间复杂度仍然很大,我们对C的这 N 种取值进行随机化,每次在对一种取值进行二分求解前先判断当前的答案这种取值能否取到。
这样就能将N次的二分降成 log N 次二分。

注:对于一个长度为n的序列 P (P1, P2 ,……, Pn ),从 P1 开始向后枚举,每次第一个找到比当前元素大的元素。在序列完全随机的情况下,找到满足条件的元素的期望个数为 log N <script type="math/tex" id="MathJax-Element-4237">N</script>个。

Code(Pascal)

var
    t,lim,i,j,k,l,o,p,n,x,y:longint;
    dq,fa,po:array[0..16000] of longint;
    le,ans,mid,ri,bj:real;
    be,ne,bc:array[0..32000] of longint;
    bz:array[0..1060000] of boolean;
    f,g:array[0..16000] of real;
function maxe(a,b:real):real;
    begin
        if a>b then exit(a)
        else exit(b);
    end;
procedure dg(o:longint;p:real);
    var
        i,l,k,u:longint;
        lj,kk,kkk,uu:real;
    begin
        f[o]:=-maxlongint;
        g[o]:=-maxlongint;
        if (bc[be[o]]=fa[o]) and (ne[be[o]]=0) then
        begin
            f[o]:=dq[o]-p;
            g[o]:=0;
            exit;
        end;
        k:=be[o];
        while k>0 do
        begin
            if bc[k]<>fa[o] then
            begin
                fa[bc[k]]:=o;
                dg(bc[k],p);
            end;
            k:=ne[k];
        end;
        k:=be[o];
        lj:=0;
        kk:=-maxlongint;
        kkk:=kk;
        while k>0 do
        begin
            u:=bc[k];
            if fa[o]<>u then
            begin
                lj:=lj+g[u];
                uu:=f[u]-g[u];
                if uu>kk then
                begin
                    kkk:=kk;
                    kk:=uu;
                end
                else
                if uu>kkk then kkk:=uu;
            end;
            k:=ne[k];
        end;
        f[o]:=lj+kk+dq[o];
        if f[o]>lj then g[o]:=f[o] else g[o]:=lj;
        if lj+kk+kkk+dq[o]+p>g[o] then g[o]:=lj+kk+kkk+dq[o]+p;
    end;
procedure sjzl;
    var
        i,k:longint;
    begin
        for i:=1 to p do
        begin
            k:=random(p div 2)+1+p div 2;
            po[0]:=po[i];
            po[i]:=po[k];
            po[k]:=po[0];
        end;
    end;
function pd(o:real):boolean;
    var
        i:longint;
    begin
        fa[1]:=-1;
        dg(1,o);
        if g[1]>=o then exit(true)
        else exit(false);
    end;
begin
    randomize;
    readln(n,lim);
    for i:=1 to n do
    read(dq[i]);
    for i:=1 to n-1 do
    begin
        readln(x,y);
        inc(o);  ne[o]:=be[x];
        be[x]:=o;  bc[o]:=y;
        inc(o); ne[o]:=be[y];
        be[y]:=o;  bc[o]:=x;
    end;
    readln(t);
    p:=2;
    po[1]:=0;
    po[2]:=t;
    for i:=1 to n do
    if lim-1-dq[i]<=t then
    if not(bz[lim-1-dq[i]]) then
    begin
        inc(p);
        po[p]:=lim-1-dq[i];
        bz[po[p]]:=true;
    end;
    bj:=n;
    bj:=maxe(bj*lim/8,100000);
    for i:=1 to 10 do
    sjzl;
    for i:=1 to p do
    begin
        for l:=1 to n do
        dq[l]:=(dq[l]+po[i]) mod lim;
        if pd(ans) then
        begin
            le:=ans;
            ri:=bj;
            while le+0.000001<ri do
            begin
                mid:=(le+ri)/2;
                if pd(mid) then le:=mid
                else ri:=mid;
            end;
            ans:=le;
        end;
        for l:=1 to n do
        dq[l]:=(dq[l]-po[i]+lim) mod lim;
    end;
    writeln(ans:0:7);
end.
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值