• NOIP2015 运输计划


    题目背景

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

    题目描述

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

    小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物

    流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰。

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

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

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

    输入输出格式

    输入格式:

    输入文件名为 transport.in。

    第一行包括两个正整数 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的物流公司完成阶段性工作所需要的最短时间。

    输入输出样例

    输入样例#1: 复制
    6 3  1 2 3  1 6 4  3 1 7  4 3 6  3 5 5  3 6  2 5  4 5
    输出样例#1: 复制
    11

    说明

    所有测试数据的范围和特点如下表所示

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

     

    思路:这题好麻烦的说。。。我们先考虑m=1的情况,先对运输路线权值求和,再对运输路线权值求最大值,两者作差,得解。

    当m!=1时,情况比较复杂,我们一步步分析。首先,因为要求最大值最小,所以我们考虑二分,令mid为当前二分的最大值,那么所有大于mid的路线都是不合法的,我们需要对路线进行删边。

     如果多条路线不合法且互不相交,那么我们无论删哪条边都不会使答案合法(想想看,为什么?)。如果存在一条边,它会被所有不合法的路线覆盖,那么它被删掉时才有可能使答案合法,答案合法

    当且仅当删去这条边后使得原来最长的路线长度小于等于mid。因为,如果不把这条边删除,那么我们选择的另一条边一定没有被所有不合法的路线覆盖,那么删去后,至少会剩余一整条不合法的路线,

    仍不能使答案合法。

    求路径交,可以用树剖+线段树(奇慢) ,还可以用树上差分,如果把树上的点映射到DFS序中,那么会更快。由于这题数据范围巨恶心,所以只能用树上差分+DFS序。

    至于lca,用树剖求或tarjan离线求。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int N=3e5+5;
    const int M=3e5+5;
    int read()
    {
        int ret=0,f=1;
        char c=getchar();
        while(c<'0'||c>'9')
        {if(c=='-') f=-1;c=getchar();}
        while(c>='0'&&c<='9')
        {ret=ret*10+c-'0';c=getchar();}
        return ret*f;
    }
    int n,m;
    struct edge{
        int from,to,dis;
    }e[M<<1];
    int head[N],nxt[M<<1],tot=0;
    void adde(int f,int t,int d)
    {
        e[++tot]=(edge){f,t,d};
        nxt[tot]=head[f];
        head[f]=tot;
    }
    int fa[N],deep[N],siz[N],hson[N],top[N],dfn[N],rank[N],w[N],tim=0;
    int tmp[N];
    void dfs1(int t,int f)
    {
        fa[t]=f;
        deep[t]=deep[f]+1;
        dfn[++tim]=t;
        for(int i=head[t];i;i=nxt[i])
        {
            int v=e[i].to;
            if(v!=f)
            {
                w[v]=e[i].dis;
                rank[v]=rank[t]+w[v];
                dfs1(v,t);
                siz[t]+=siz[v];
                if(!hson[t]||siz[v]>siz[hson[t]]) hson[t]=v;
            }
        }
    }
    void dfs2(int t,int tp)
    {
        top[t]=tp;
        if(!hson[t]) return;
        dfs2(hson[t],tp);
        for(int i=head[t];i;i=nxt[i])
        {
            int v=e[i].to;
            if(v!=fa[t]&&v!=hson[t]) dfs2(v,v);
        }
    }
    int lca(int x,int y)
    {
        int fx=top[x],fy=top[y];
        while(fx!=fy)
        {
            if(deep[fx]<deep[fy]) swap(x,y),swap(fx,fy);
            x=fa[fx],fx=top[x];
        }
        return deep[x]<deep[y]?x:y;
    }
    struct node{
        int from,to,lcaa,dis;
    }p[M];
    bool check(int mid)
    {
        memset(tmp,0,sizeof(tmp));
        int cnt=0;
        int D=0;
        for(int i=1;i<=m;i++)
        {
            if(p[i].dis>mid)
            {
                cnt++;
                D=max(D,p[i].dis);
                tmp[p[i].from]++,tmp[p[i].to]++;
                tmp[p[i].lcaa]-=2;
            }
        }
        if(!cnt) return true;
        for(int i=n;i>=1;i--) tmp[fa[dfn[i]]]+=tmp[dfn[i]];
        for(int i=1;i<=n;i++)
        {
            if(tmp[i]==cnt&&D-w[i]<=mid) return true;
        }
        return false;
    }
    int main()
    {
        n=read(),m=read();
        int a,b,c;
        for(int i=1;i<=n-1;i++)
        {
            a=read(),b=read(),c=read();
            adde(a,b,c);
            adde(b,a,c);
        }
        dfs1(1,0);
        dfs2(1,1);
        for(int i=1;i<=m;i++)
        {
            a=read(),b=read();
            p[i].from=a,p[i].to=b;
            p[i].lcaa=lca(a,b);
            p[i].dis=rank[a]+rank[b]-2*rank[p[i].lcaa];
            
        }
        int l=0,r=1e9+7,mid;
        while(r-l>1)
        {
            mid=l+r>>1;
            if(check(mid)) r=mid;
            else l=mid;
        }
        printf("%d
    ",r);
        return 0;
    }
  • 相关阅读:
    6月15日学习日志
    6月14日学习日志
    6月13日学习日志
    6月12日学习日志
    给建民哥的意见
    6月10日学习日志
    6月9日学习日志
    6月8日学习日志
    梦断代码读书笔记3
    第二次冲刺(六)
  • 原文地址:https://www.cnblogs.com/loi-frank/p/7711007.html
Copyright © 2020-2023  润新知