P5021 赛道修建

题意:

  • 给你一珂树。
  • 让你找出一些路径使得最小的路径长度的最大。
  • 路径要求边不相交,点可以相交。

\(80\) 分前置做法

首先考虑二分。假设二分的值是 \(goal\)

left:=0; root:=1;
    while left<=right do
    begin
        fillchar(val,sizeof(val),0); // val 数组代表每一个点能给父亲的最优贡献
        mid:=(left+right) >> 1;
        tmp:=Check(mid,root,root);
        if tmp>=m then begin left:=mid+1; ans:=mid end else right:=mid-1;
    end;
writeln(ans);

我们考虑到,一个节点的儿子可能会给自己带来一些路径,把这些路径存起来,设为 \(rope\) 数组。

\(rope\) 中,如果有一条路径 \(\ge goal\) 的话可以自己匹配,\(ans++\)。如果不能自己匹配,那么我们就把它跟别人匹配,如果可以那么 \(ans++\)。因为时间的限制,上面的操作时可以先排一个序然后操作的。

(\(11\) 直接选,而 \(9\)\(1\) 加起来也可以 \(\ge goal\))

  • 问题 \(1\) 为什么要两两匹配?

一个树上路径就是一条线,而一条线经过一个点只能连出另一条边,不能连多条边。

  • 问题 \(2\) 为什么 \(\ge goal\) 可以直接选?

它跟别人再次匹配就是葬送了别人,所以最好自个匹配掉。

function Check(mid,x,fa:longint):longint;
var i,l:longint;
begin
    i:=cnt[x]; Check:=0;
    while i<>-1 do // 先跑儿子
    begin
        if reach[i]<>fa then inc(Check,Check(mid,reach[i],x));
        i:=next[i];
    end;

    i:=cnt[x]; tail:=0; // 把所有路径取下来,其中如果儿子有路径升上来,那么久连着边一起加上来
    while i<>-1 do
    begin
        if reach[i]<>fa then
        begin
            inc(tail);
            rope[tail]:=val[reach[i]]+value[i];
        end;
        i:=next[i];
    end;
    Sort(1,tail); // 手动
    for i:=tail downto 1 do
        if rope[i]>=mid then begin inc(Check); dec(tail); end else break; 
        // 自己直接匹配,注意这里有单调性所以可以直接 break
    l:=1;
    for i:=tail downto 1 do // 互相匹配,如果两两加起来可以的话就弄
    begin
        while (rope[i]+rope[l]<mid)and(l<tail+1) do inc(l); // 用指针搞
        if l<i then begin inc(Check); rope[i]:=0; rope[l]:=0; end else break; 
        // 匹配过就不要了 rope[i/l]=0
    end;
    for i:=tail downto 1 do val[x]:=max(val[x],rope[i]); 
    // 找最大的(且没有被匹配过)给父亲
    exit(check);
end;

\(100\) 做法

看完上面那个你会发现你得到了 \(80\) 的高分部分分。

错误原因在于 你留给父亲的只是你那些边匹配剩下的,对你的父亲很不利。所以 \(80\) 就是贪心。

首先先把那些 \(\ge goal\) 的匹配掉,原因如上述。

其余的就进行一次二分答案看留哪个路径给父亲好,如果我留了这条边我还能保证自己的答案贡献不变,那很好,其它的给父亲就 \(OK\)

为了方便(X\(i\)n\(Y\)a\(n\)g),我们定义一个 \(Judge(goal,choose,tail)\) 代表在 \(1\)~\(tail\) 内不匹配 \(rope[choose]\) 达到 \(goal\) 的最大匹配数量。满足单调性很好搞。

function Judge(goal,choose,tail:longint):longint;
var l,r:longint;
begin
    l:=0; Judge:=0;
    for r:=tail downto 1 do
    begin
        if r=choose then continue;
        if l=choose then inc(l);
        repeat
            inc(l); if l=choose then inc(l);
        until (rope[r]+rope[l]>=goal)or(l>tail);
        if l<r then inc(Judge) else break;
    end;
end;

然后看一下 \(Check\):

function Check(goal,x,fa:longint):longint;
var i,l,ans,mid,tmp:longint;
begin
    i:=cnt[x]; Check:=0;
    while i<>-1 do
    begin
        if reach[i]<>fa then inc(Check,Check(goal,reach[i],x));
        i:=next[i];
    end;

    i:=cnt[x]; tail:=0;
    while i<>-1 do
    begin
        if reach[i]<>fa then
        begin
            inc(tail);
            rope[tail]:=val[reach[i]]+value[i];
        end;
        i:=next[i];
    end;

    Sort(1,tail);
    for i:=tail downto 1 do
    	if rope[i]>=goal then begin inc(Check); dec(tail); end else break;
    // 单独的匹配掉
    tmp:=Judge(goal,-1,tail); // 全部都选可以匹配多少
    inc(Check,tmp); // 答案+=tmp

    l:=1; r:=tail; ans:=0; 
    while l<=r do
    begin
    	mid:=(l+r) >> 1; // 我留 rope[mid] 给父亲
    	if Judge(goal,mid,tail)=tmp then begin l:=mid+1; ans:=rope[mid]; end else r:=mid-1;
        // 坑点注意:由于可能没有路径父亲,所以这里的 ans 不能存 mid,只能存 rope[mid]
    end;
    val[x]:=ans;
end;

非正确的复杂度分析

\(\sqrt{N}\) 个节点每一个节点有 \(\sqrt{N}\) 个儿子(平摊?)。

  • 排序 \(+\) 各种 \(N \times \sqrt{N} \log \sqrt{N}\)
  • 外面一层是 \(\log \sum\limits^{N}_{i=1}l_i\)

所以应该是 \(N \sqrt{N} \log \sqrt{N} \log \sum\limits^{N}_{i=1}l_i\)

按照大家说的 \(N \log N \log \frac{\sum\limits^{N}_{i=1}l_i}{M}\) 就好了。

posted @ 2019-01-25 21:50  _ARFA  阅读(134)  评论(0编辑  收藏  举报