zoukankan      html  css  js  c++  java
  • 【数据结构】运输计划 NOIP2015提高组D2T3

    【数据结构】运输计划 NOIP2015提高组D2T3

    >>>>题目

    【题目描述】

    公元 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,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1<=ai,bi<=n 且 0<=ti<=1000。接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1<=ui,vi<=n

    【输出描述】
    输出文件只包含一个整数,表示小 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
    【样例解释】

    将第 1 条航道改造成虫洞: 则三个计划耗时分别为:11,12,11,故需要花费的时间为 12。

    将第 2 条航道改造成虫洞: 则三个计划耗时分别为:7,15,11,故需要花费的时间为 15。

    将第 3 条航道改造成虫洞: 则三个计划耗时分别为:4,8,11,故需要花费的时间为 11。

    将第 4 条航道改造成虫洞: 则三个计划耗时分别为:11,15,5,故需要花费的时间为 15。

    将第 5 条航道改造成虫洞: 则三个计划耗时分别为:11,10,6,故需要花费的时间为 11。

    故将第 3 条或第 5 条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为 11。

    >>>>分析

    此题必备技能: 二分+树上差分+LCA

    本题思路

    二分查找,check里面记录所有权值大于mid的路径,并求出这样的路径的数量(用cnt表示)

    在这个过程中,求出所有路径”与mid的差“的最大值,就是最大超出的时间(用ans表示)

    再找出一条能覆盖所有“大于mid的路径“的边,即为题目描述的虫洞

    {

           如果没有这样的边,那么不管怎样减总有至少一个任务的时间超过mid ,return false;

           如果有这样的边,

           {

                       for(每一条边)

                       如果(这条边被每一条大于mid的路径经过&&这条边权值大于ans)  也就是说减去这条边可以将每一条大于mid的路径减到mid下

                       return true;

           }
    }

    具体分析

    (1)二分 :更多时间能完成任务,那么更短时间也能完成,具有单调性。

    (2)树上差分:

       tmp[i]表示:表示i这个点通往父亲的边,记录这条边被遍历的次数 

       num[i]表示:以i为根节点所有子树的tmp值

       dis[i]表示:i 到根的距离

       对于每一条链s(起点),t(终点),将s,t的tmp值+1;,将LCA(s,t)的tmp值-2

       一个点最终被覆盖的次数就是这个点的num值(用dfs序维护)

    (3)求LCA:  我这里选择的是倍增求LCA,真的是模板yo

    (4)预处理:预处理出每一条链的链长和每一条链的LCA(这个说法好像不大对)

     >>>>代码

    #include<bits/stdc++.h>
    #define maxn 300005
    using namespace std;
    int len=-1,sum=-1;
    int n,m,tot,times;
    int head[maxn],to[2*maxn],nxt[2*maxn],val[2*maxn];
    int dep[maxn],dis[maxn],fa[maxn][26],tmp[maxn],id[maxn];
    struct node
    {
        int u,v,lc,d;
    }ed[maxn];
    void read(int &x)
    {
        x=0;int f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-') f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}x=x*f;
    }
    void add(int u,int v,int p)//前向星存双向边 
    {
        to[++tot]=v;
        nxt[tot]=head[u];
        val[tot]=p;
        head[u]=tot;
    }
    int lca(int x,int y)//倍增求lca 
    {
        if(dep[x]<dep[y]) swap(x,y);//保证x在更深的地方 
        for(int i=25;i>=0;--i)
        if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
        if(x==y) return x;
        for(int i=25;i>=0;--i)//两个一起往上跳 
        {
            if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
        }
        return fa[x][0];
    }
    void dfs(int u,int father) 
    {
        id[++times]=u;//dfs序 
        for(int i=1;i<=25;++i) fa[u][i]=fa[fa[u][i-1]][i-1];//预处理倍增数组 
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(v==father) continue;
            fa[v][0]=u;
            dis[v]=dis[u]+val[i];
            dep[v]=dep[u]+1;
            dfs(v,u);
        }
    }
    bool check(int mid)
    {
        int cnt=0,ans=-1;
        memset(tmp,0,sizeof(tmp));
        for(int i=1;i<=m;++i)
        {
            if(ed[i].d>mid)
            {
                tmp[ed[i].u]++;//点到父亲边的出现次数 
                tmp[ed[i].v]++;
                tmp[ed[i].lc]-=2;
                ans=max(ans,ed[i].d-mid);//找到“最大超出时限” 
                cnt++;
            }
        }
        if(cnt==0) return true;//所有边都小于mid 
        for(int i=n;i>=1;--i) tmp[fa[id[i]][0]]+=tmp[id[i]];
        //为什么要倒序?从更接近叶子节点的地方开始,更新父节点经过的次数 
        for(int i=2;i<=n;++i) //见解析 
        if(tmp[i]==cnt&&dis[i]-dis[fa[i][0]]>=ans) return true;
        return false;
    }
    int find(int l,int r)
    {
        int ans;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        return ans;
    }
    int main()
    {
    //    freopen("transport.in","r",stdin);
    //    freopen("transport.out","w",stdout);
        read(n),read(m);
        for(int i=1;i<=n-1;++i)
        {
            int x,y,z;
            read(x),read(y),read(z);
            add(x,y,z),add(y,x,z);
            sum=max(sum,z);
        }
        dis[1]=0; dep[1]=1;
        dfs(1,1);
        for(int i=1;i<=m;++i)
        {
            read(ed[i].u),read(ed[i].v);
            ed[i].lc=lca(ed[i].u,ed[i].v);
            ed[i].d=dis[ed[i].u]+dis[ed[i].v]-2*dis[ed[i].lc];//求u到v的链长 
            len=max(len,ed[i].d);
        }
        printf("%d
    ",find(len-sum,len));//二分 所用最短时间 
        return 0;/**/
    }
    /*
    6 3
    1 2 3
    1 6 4
    3 1 7
    4 3 6
    3 5 5
    3 6
    2 5
    4 5
    */
    戳这里

    思路借鉴 洛谷大佬:@D_14134

    完结撒花!

  • 相关阅读:
    .net注册iis
    hdu 1081To The Max
    hdu 1312Red and Black
    hdu 1016Prime Ring Problem
    hdu 1159Common Subsequence
    hdu 1372Knight Moves
    hdu 1686Oulipo
    hdu 1241Oil Deposits
    hdu 1171Big Event in HDU
    hdu 4006The kth great number
  • 原文地址:https://www.cnblogs.com/psyyyyyy/p/10608799.html
Copyright © 2011-2022 走看看