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

    题目描述

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

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

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

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

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

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

    Input

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

    接下来 (n-1) 行描述航道的建设情况,其中第 (i) 行包含三个整数 (a_i, b_i)(t_i),表示第(i)条双向航道修建在 (a_i)(b_i) 两个星球之间,任意飞船驶过它所花费的时间为 (t_i)

    接下来 (m) 行描述运输计划的情况,其中第 (j) 行包含两个正整数 (u_j)(v_j),表示第 (j)个 运输计划是从 (u_j) 号星球飞往 (v_j) 号星球。

    数据保证 (1≤u_i,v_i≤n,1<=n,m<=300000)

    数据保证 (1≤a_i,b_i≤n)(0≤t_i≤1000)

    Output

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

    Sample Input

    6 3 
    1 2 3 
    1 6 4 
    3 1 7 
    4 3 6 
    3 5 5 
    3 6 
    2 5 
    4 5
    

    Sample Output

    11
    

    首先,看到了最大值最小就很自然的想到了二分答案。

    嗯,二分是很显然的。。。。

    那么,我们该如何(check)呢?,对于两点间路径已经小于当前(x)的路径可以直接忽视掉。

    那么剩下来的路径就是大于当前(x)的路径了,对于这些路径我们需要去掉一条边是所有的路径中最大值最小。

    即,我们需要减去路径交集中最大的边

    这个操作是很显然的,因为只有减得越多才可以剩的越少。

    现在,我们需要做的就是求出所有路径的交集了?

    怎么求呢?我们可以利用树上差分来进行求解。

    我们需要将边转换到点上,那么我们该如何转换呢?

    由于一条边的节点一定是父亲节点和儿子节点之分。

    而儿子节点所对应的边只有一条,所以我们将边转移到子节点上。

    当加入一条路径(a,b)时,设(LCA)(a,b)(lca),则(Sum[a]++,Sum[b]++,Sum[LCA]-=2)

    我们统计答案时若当前节点的(Sum)值为不满足条件的路径个数,说明该节点所对应的边为所有路径的交集,

    更新答案即可。。。

    最后看一下最长的路径减去路径上的最大值是否(<=)当前(x)即可。

    不过这道题的数据较大,普通的算法容易被卡掉。

    那么,我们就需要一些优化。。。

    1.优化二分上下界:

    (L=)最长的路径长度减去最大的边权,(R=)最长的路径长度。

    这样二分的次数保证在(10)以内。

    2.避免递归:

    由于统计答案时,我们需要递归求解,而递归是有常数的,

    我们又发现一个节点的值只和其子树有关,而子树在(dfs)序中时保证子节点编号大于根节点编号。

    于是,我们可以利用(dfs)序倒着更新(Sum)值。

    代码如下

    #include <bits/stdc++.h>
     
    using namespace std;
     
    #define LL long long
    #define reg register
    #define Raed Read
    #define debug(x) cerr<<#x<<" = "<<x<<endl;
    #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
    #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
    #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
    #define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
     
    inline char G1() {
        static const int LEN=2000005;
        static char U[LEN],*T=U,*E=U;
        if(T==E)T=U,E=U+fread(U,1,LEN,stdin);
        return T==E?EOF:*T++;
    }
     
    inline int Read(void) {
        int res=0,f=1;
        char c;
        while(c=G1(),c<48||c>57)if(c=='-')f=0;
        do res=(res<<3)+(res<<1)+(c^48);
        while(c=G1(),c>=48&&c<=57);
        return f?res:-res;
    }
     
    template<class T>inline bool Min(T &a,T const&b) {
        return a>b ?a=b,1:0;
    }
    template<class T>inline bool Max(T &a,T const&b) {
        return a<b?a=b,1:0;
    }
     
    const int N=3e5+5,M=3e5+5,mod=1e9+7;
     
    bool MOP1;
     
    int n,m,A[N],B[N],TT[M];
     
    struct Link_list {
        int Tot,Head[N],to[M<<1],Nxt[M<<1],cost[M<<1];
        inline void clear(void) {
            Tot=0;
            memset(Head,0,sizeof Head);
        }
        inline void AddEdgepair(int a,int b,int c) {
            to[++Tot]=b,cost[Tot]=c,Nxt[Tot]=Head[a],Head[a]=Tot;
            to[++Tot]=a,cost[Tot]=c,Nxt[Tot]=Head[b],Head[b]=Tot;
        }
    } G;
     
    int Fa[M],dep[M],top[M],sz[M],son[M],Dis[M],LCA[N],cnt,Id[N];
     
    void dfs1(int x,int f) {
        Id[++cnt]=x;
        dep[x]=dep[f]+1,Fa[x]=f,sz[x]=1;
        erep(i,G,x) {
            int y=G.to[i];
            if(y==f)continue;
            Dis[y]=Dis[x]+G.cost[i];
            TT[y]=G.cost[i];
            dfs1(y,x),sz[x]+=sz[y];
            if(sz[y]>sz[son[x]])son[x]=y;
        }
    }
     
    void dfs2(int x,int f) {
        top[x]=f;
        if(son[x])dfs2(son[x],f);
        else return;
        erep(i,G,x) {
            int y=G.to[i];
            if(y==Fa[x]||y==son[x])continue;
            dfs2(y,y);
        }
    }
     
    inline int lca(int x,int y) {
        while(top[x]!=top[y]) {
            if(dep[top[x]]>dep[top[y]])x=Fa[top[x]];
            else y=Fa[top[y]];
        }
        return dep[x]<dep[y]?x:y;
    }
     
    int res,tot,Sum[N];
     
    inline bool check(int x) {
        res=-1,tot=0;
        rep(i,1,n)Sum[i]=0;
        rep(i,1,m) {
            if(Dis[A[i]]+Dis[B[i]]-2*Dis[LCA[i]]<=x)continue;
            tot++,Sum[A[i]]++,Sum[B[i]]++,Sum[LCA[i]]-=2;
        }
        drep(i,n,1) {
            int u=Id[i];
            erep(i,G,u) {
                int y=G.to[i];
                if(y==Fa[u])continue;
                Sum[u]+=Sum[y];
            }
            if(Sum[u]==tot)Max(res,TT[u]);
        }
        if(res==-1)return false;
        rep(i,1,m) {
            if(Dis[A[i]]+Dis[B[i]]-2*Dis[LCA[i]]<=x)continue;
            if(Dis[A[i]]+Dis[B[i]]-2*Dis[LCA[i]]-res>x)return false;
        }
        return true;
    }
     
    int Ma;
     
    inline void solve(void) {
        int L=0,R=0,Ans=0;
        rep(i,1,m)Max(R,Dis[A[i]]+Dis[B[i]]-2*Dis[LCA[i]]);
        L=R-Ma;
        while(L<=R) {
            int mid=(L+R)>>1;
            if(check(mid))Ans=mid,R=mid-1;
            else L=mid+1;
        }
        printf("%d
    ",Ans);
    }
     
     
    bool MOP2;
     
    inline void _main(void) {
        n=Read(),m=Read();
        int f=1;
        ret(i,1,n) {
            int a=Read(),b=Read(),c=Read();
            Max(Ma,c),G.AddEdgepair(a,b,c);
        }
        dfs1(1,0),dfs2(1,1);
        rep(i,1,m)A[i]=Read(),B[i]=Read(),LCA[i]=lca(A[i],B[i]);
        solve();
    }
     
    signed main() {
        _main();
        return 0;
    }
    
  • 相关阅读:
    photoshop--图像大小(分辨率和宽高)
    Arduino--单向倾斜开关
    禁止google浏览器http链接强制跳转为https
    属性缓存_使用ConcurrentDictionary替代Hashtable对多线程的对象缓存处理
    .Net(windows服务器)清理缓存数据的几个方法
    【西天取经】(升级.net5)用了一整天才把项目从.netcoreapp3.1升级到.net5(转载)
    在Debian 9 vim中启用鼠标复制粘贴
    将数据批量插入SQL Server
    shell 脚本中 在grep -E '($wo|$ni|$ta)' 这里面用变量
    kubernetes 重启的几种方法
  • 原文地址:https://www.cnblogs.com/dsjkafdsaf/p/11360357.html
Copyright © 2011-2022 走看看