zoukankan      html  css  js  c++  java
  • 运输计划

    【题目描述】

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

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

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

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

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

    【输入描述】

    第一行包括两个正整数n、m,表示L国中星球的数量及小P公司预接的运输计划的数量,星球从1到n编号;

    接下来n-1行描述航道的建设情况,其中第i行包含三个整数ai、bi和ti,表示第i条双向航道修建在ai与bi两个星球之间,任意飞船驶过它所花费的时间为ti。接下来m行描述运输计划的情况,其中第j行包含两个正整数uj和vj,表示第j个运输计划是从uj号星球飞往vj号星球。

    【输出描述】

    共1行,包含1个整数,表示小P的物流公司完成阶段性工作所需要的最短时间。

    【输入样例】

    6 3

    1 2 3

    1 6 4

    3 1 7

    4 3 6

    3 5 5

    3 6

    2 5

    4 5

    【输出样例】

    11

    【数据范围及提示】

    请注意常数因子带来的程序效率上的影响。

    95分(二分+LCA):

    源代码:
    
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define Maxn 300001
    using namespace std;
    struct Node
    {
        int X,Y,Ans,LCA;
    }P[Maxn];
    int F[20][Maxn],Next[2*Maxn],Head[Maxn],/*第一邻接点*/To[2*Maxn],/*邻接表*/Length[2*Maxn]/*边权(时间)*/;
    int w[Maxn],sum[Maxn],Num[Maxn],G[Maxn],Q[Maxn],/*广搜队列*/Deep[Maxn];/*结点的深度*/
    int m,n,num;
    void Add(int t1,int t2,int t) //邻接表形式加边。
    {
        num++;
        To[num]=t2;
        Length[num]=t;
        Next[num]=Head[t1];
        Head[t1]=num;
    }
    int Up(int t,int D) //结点上调。
    {
        for (int a=0;a<=18;a++)
          if (D&(1<<a))
            t=F[a][t];
        return t;
    }
    int LCA(int t1,int t2) //计算最近公共祖先。
    {
        if (Deep[t1]>Deep[t2])
          t1=Up(t1,Deep[t1]-Deep[t2]);
        else
          t2=Up(t2,Deep[t2]-Deep[t1]);
        if (t1==t2)
          return t1;
        for (int a=18;a>=0;a--)
          if (F[a][t1]!=F[a][t2])
          {
              t1=F[a][t1];
            t2=F[a][t2];
          }
        return G[t1];
    }
    bool Rule(Node t1,Node t2) //降序排。
    {
        return t1.Ans>t2.Ans;
    }
    bool Check(int t) //判断t是否满足要求。
    {
        int num=0;
        memset(w,0,sizeof(w));
        memset(Num,0,sizeof(Num));
        for (int a=1;a<=m;a++) //运输计划P已经按总耗时降序排,计算耗时超过t的运输计划数量,并统计所涉结点的次数。
          if (P[a].Ans>t)
          {
            w[P[a].X]++;
            w[P[a].Y]++;
            w[P[a].LCA]-=2;
            num++;
          }
          else
            break; //已降序排,不超过t的不用再处理。
        if (!num)
          return true; //都不超t,True。
        for (int a=n;a>=1;a--)
        { //自下而上统计:以结点T为根结点的子树中,耗时超过t的运输计划中所涉结点的个数。
            int T=Q[a];
            Num[T]=w[T];
            for (int b=Head[T];b;b=Next[b])
              if (To[b]!=G[T])
                Num[T]+=Num[To[b]];    //累加子结点的Num值。
        }
        for (int a=1;a<=n;a++) //自根结点开始向下判断。
        {
            int T=Q[a];
            for (int b=Head[T];b;b=Next[b])
              if (To[b]!=G[T]) //如果以结点To[b]为根结点的子树中,耗时超过t的结点个数恰是num个,且去掉第b条边之后的耗时不超过t,则True。
                if (Num[To[b]]==num&&Length[b]>=P[1].Ans-t)
                  return true;
        }
        return false;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int a=1;a<n;a++)
        {
            int t,t1,t2;
            scanf("%d%d%d",&t1,&t2,&t);
            Add(t1,t2,t);
            Add(t2,t1,t); //把边加到邻接表。
        }
        int Left=0,Right=1;
        Q[1]=1;
        G[1]=0;
        Deep[1]=0; //根结点1入队。
        while (Left!=Right) //广搜。
        {
            int t=Q[++Left];
            for (int a=Head[t];a;a=Next[a])
              if (To[a]!=G[t]) //计算父结点、深度及到根结点的权值。
              {
                  G[To[a]]=t;
                Deep[To[a]]=Deep[t]+1;
                sum[To[a]]=sum[t]+Length[a];
                  Q[++Right]=To[a];
              }
        }
        for (int a=1;a<=n;a++)
          F[0][a]=G[a];
        for (int a=1;a<=18;a++) //倍增求F(a,b),结点j的2^i祖先。
          for (int b=1;b<=n;b++)
            if (F[a-1][b])
              F[a][b]=F[a-1][F[a-1][b]];
            else
              F[a][b]=0;
        int Max;
        for (int a=1;a<=m;a++)
        {
            scanf("%d%d",&P[a].X,&P[a].Y); //读入运输计划。
            P[a].LCA=LCA(P[a].X,P[a].Y); //求两端点的LCA、该运输计划的总耗时。
            P[a].Ans=sum[P[a].X]+sum[P[a].Y]-2*sum[P[a].LCA];
            Max=max(Max,P[a].Ans); //维护最大值。
        }
        sort(P+1,P+m+1,Rule); //运输计划按总耗时降序排。
        Left=0;
        Right=Max+1;
        int ans; //[0,Max+1)区间中二分。
        while (Left<=Right)
        {
            int t=(Left+Right)/2;
            if (Check(t))
            {
                ans=t;
                Right=t-1;
            }
            else
              Left=t+1;
        }
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    从零开始——PowerShell应用入门(全例子入门讲解)
    详解C# Tuple VS ValueTuple(元组类 VS 值元组)
    How To Configure VMware fencing using fence_vmware_soap in RHEL High Availability Add On——RHEL Pacemaker中配置STONITH
    DB太大?一键帮你收缩所有DB文件大小(Shrink Files for All Databases in SQL Server)
    SQL Server on Red Hat Enterprise Linux——RHEL上的SQL Server(全截图)
    SQL Server on Ubuntu——Ubuntu上的SQL Server(全截图)
    微软SQL Server认证最新信息(17年5月22日更新),感兴趣的进来看看哟
    Configure Always On Availability Group for SQL Server on RHEL——Red Hat Enterprise Linux上配置SQL Server Always On Availability Group
    3分钟带你了解PowerShell发展历程——PowerShell各版本资料整理
    由Find All References引发的思考。,
  • 原文地址:https://www.cnblogs.com/Ackermann/p/5783401.html
Copyright © 2011-2022 走看看