• 树型大融合——NOIP提高组2015 D1T3 【运输计划】


    下午用一个小时看了一下树上差分,打了个差分模板,A了3题,真的爽!

    题目描述:

    公元2044 年,人类进入了宇宙纪元。

    L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球。

    小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰。

    为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

    在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。

    如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?

    数据范围:

    n<=300000,m<=300000

    思路分析:

    首先得写个倍增求LCA求个链长,这应该没问题吧,我之前讲过的(不懂戳这里,你们可能会惊奇地发现下面的代码里我的lca是复的,嘘——)。

    那么再看问题,“使最大的运输时间最少”——某王  定理之二十一(滑稽):任何求最大值最小,最小值最大的问题十有八九是二分答案。

    嗯,很好,让我们考虑二分答案这种神奇的东西。

    对于我们二分出的答案mid,我们应该怎么来判断它是否合法呢?

    首先让我们找出这m条链中长度大于mid的,求出链的数量num以及最长链的长度len。

    显然,当这num条边中都经过一条长度至少为(len-mid)的边,那么当我们删去这条边后,这个答案就是合法的,因为原本所有长度大于mid的链长度都会小于等于mid。

    那么我们应该怎么找这num条链都经过的边呢?——树上差分嘛,没有争议的(不懂的话戳这里)。

    代码实现:

    var
      f:array[0..300000,0..20]of longint;
      vis:array[0..300000]of boolean;
      next,vet,dist:array[0..600000]of longint;
      head,cnt,depth,g,d,len,u,v,grand:array[0..300000]of longint;
      ans,i,j,n,m,tot,x,y,z,max,num,k,l,r,mid:longint;
      flag:boolean;
    procedure add(x,y,z:longint);
    begin
      inc(tot);
      next[tot]:=head[x];
      vet[tot]:
    =y;
      head[x]:
    =tot;   dist[tot]:=z; end; procedure dfs(u,dep,dis:longint); var   i,v:longint; begin   depth[u]:=dep; vis[u]:=true;   for i:=1 to 20 do     f[u,i]:=f[f[u,i-1],i-1];   i:=head[u];   while i<>0 do   begin
        v:
    =vet[i];     if not vis[v] then     begin       f[v,0]:=u;       d[v]:=dis+dist[i];       dfs(v,dep+1,d[v]);     end;   i:=next[i];   end; end; function lca(a,b:longint):longint; var   i,t:longint; begin   if depth[a]>depth[b] then begin t:=a; a:=b; b:=t; end;   for i:=20 downto 0 do     if depth[f[b,i]]>=depth[a] then b:=f[b,i];
      if a=b then exit(a);   for i:=20 downto 0 do
      if f[a,i]<>f[b,i] then     begin a:=f[a,i]; b:=f[b,i]; end;   exit(f[a,0]); end; procedure getans(u,father:longint); var   i,v:longint; begin   g[u]:=cnt[u];   i:=head[u];   while i<>0 do
      begin     v:=vet[i];     if v<>father then     begin
          getans(v,u);       g[u]:
    =g[u]+g[v];       if (g[v]=num)and(dist[i]>=k) then flag:=true;     end;     i:=next[i];
      end; end; function check(x:longint):boolean; var   i:longint; begin   flag:=false; max:=0; num:=0;   fillchar(g,sizeof(g),0);   fillchar(cnt,sizeof(cnt),0);   for i:=1 to m do   if len[i]>x then   begin     inc(cnt[u[i]]); inc(cnt[v[i]]);     cnt[grand[i]]:=cnt[grand[i]]-2;     inc(num);     if len[i]>max then max:=len[i];   end;   k:=max-x;   getans(1,0);   exit(flag); end; begin   read(n,m);   for i:=1 to n-1 do   begin     read(x,y,z);     add(x,y,z); add(y,x,z);   end;   dfs(1,1,0);   for i:=1 to m do   begin
        read(u[i],v[i]);     grand[i]:
    =lca(u[i],v[i]);     len[i]:=d[u[i]]+d[v[i]]-2*d[grand[i]]; //有没有觉得我今天的码风格外清新,嗯,我用Guide写的代码。     if len[i]>max then max:=len[i];   end;   l:=1; r:=max;   while l<=r do   begin     mid:=(l+r)div 2;     if check(mid) then begin r:=mid-1; ans:=mid; end else l:=mid+1;   end;
      writeln(ans);
    end.
  • 相关阅读:
    只要肯下功夫,十岁也能学得会的 Docker 精简版!
    sprintf和sscanf的用法
    ubuntu在线安装vscode
    Makefile模板
    本地Git配置绑定远程Github账户
    mysql多表查询
    VS error 2019 错误
    Oracle--约束
    Oracle--增删查
    Oracle--子查询
  • 原文地址:https://www.cnblogs.com/WR-Eternity/p/9884897.html
Copyright © 2020-2023  润新知