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

    标签: 二分 倍增 差分 LCA


    题目描述

      公元 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,m<=300000
    接下来 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
    

    分析

    要求的就是,把树上的一条边的权值设为(0)之后,所有路径中的最大值的最小值。
    首先二分最大值,假设某次二分的最大值为(x),我们首先找出所有大于(x)的路径(也就是我们需要通过改权缩短的路径),并把路径上的所有边都标记一下。
    在标记完成后,枚举所有边,如果存在一条边位于所有长度大于于(x)的路径上,并且删除之后能使所有路径都满足(length<=x),则返回(true),否则(false)
    还有一个问题就是,对于某个路径,如何快速标记出他经过的所有边呢?我们可以使用差分,用树上前缀和来表示某个点被标记次数。比如某条边(E(u,v)),先把(sum[u]+=1,sum[v]+=1),然后(sum[lca(u,v)]-=2)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int maxn=300050;
    struct io
    {
        char op[10000000],* s;
        io(){fread(s=op,1,1<<26,stdin);}
        inline int read()
        {
            register int u = 0;
            while(*s<48) s++;
            while(*s>32)
                u=u*10+* s++ -48;
            return u;
        }
    } ip;
    #define read ip.read
    int n,m;
    struct Edge
    {
        int v,w,nxt;
    }e[maxn*2];
    int h[maxn],tot;
    inline void addEdge(int x,int y,int w){
        e[++tot]=(Edge){y,w,h[x]};
        h[x]=tot;
    }
     
    int f[maxn][20],dis[maxn],dep[maxn],w[maxn];
    int id[maxn],cnt;   
    void dfs(int x,int x_fa){
        id[++cnt]=x;
        f[x][0]=x_fa;
        dep[x]=dep[x_fa]+1;
        for (int i = 1; f[x][i-1] ; ++i)
        {
            f[x][i]=f[f[x][i-1]][i-1];
        }
        for (int i = h[x]; i ; i=e[i].nxt)
        {
            if(e[i].v!=x_fa){
                dis[e[i].v]=dis[x]+e[i].w;
                w[e[i].v]=e[i].w;
                dfs(e[i].v,x);
            }
        }
    }
     
    int u[maxn],v[maxn],a[maxn],len[maxn];
    inline int lca(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        int t=dep[x]-dep[y];
        for (int i = 0; t ; ++i)
        {
            if(t&(1<<i)) x=f[x][i],t^=(1<<i);
        }
        if(x==y) return x;
        for (int i = 19; i >= 0; --i)
        {
            if(f[x][i]!=f[y][i]){
                x=f[x][i];
                y=f[y][i];
            }
        }
        return f[x][0];
    }
     
    int sum[maxn];
    inline bool g(int x){
        memset(sum,0,sizeof sum);
        int M_len=0,c=0;
        for (int i = 0; i < m; ++i)
        {
            if(len[i]>x){
                M_len=max(M_len,len[i]);
                c++;
                sum[u[i]]++;
                sum[v[i]]++;
                sum[a[i]]-=2;
            }
        }
        for (int i = n; i ; --i)
        {
            sum[f[id[i]][0]]+=sum[id[i]];
        }
        for (int i = 1; i <= n; ++i)
        {
            if(sum[i]>=c&&M_len-w[i]<=x) return true;
        }
        return false;
    }
    int main(int argc, char const *argv[])
    {
        // scanf("%d%d", &n,&m);
        n=read(),m=read();
        for (int i = 0; i < n-1; ++i)
        {
            int x,y,w;
            x=read(),y=read(),w=read();
            // scanf("%d%d%d", &x,&y,&w);
            addEdge(x,y,w);
            addEdge(y,x,w);
        }
        dfs(1,0);
        int l=0,r=0;
        for (int i = 0; i < m; ++i)
        {
            u[i]=read(),v[i]=read();
            // scanf("%d%d", u+i,v+i);
            a[i]=lca(u[i],v[i]);
            len[i]=dis[u[i]]+dis[v[i]]-2*dis[a[i]];
            r=max(r,len[i]);
        }
        int ans;
        while(l<=r){
            int mid=(l+r)>>1;
            if(g(mid)) ans=mid,r=mid-1;
            else l=mid+1; 
        }
        printf("%d
    ", ans);
        return 0;
    }
    /**************************************************************
        Problem: 1754
        User: upc_reserver201706
        Language: C++
        Result: 正确
        Time:916 ms
        Memory:58224 kb
    ****************************************************************/
    
  • 相关阅读:
    [moka同学笔记]八、Yii2.0课程笔记(魏曦老师教程)[授权]
    [moka同学转载]Yii2 中国省市区三级联动
    [moka同学笔记]四、Yii2.0课程笔记(魏曦老师教程)[匿名函数的使用操作]
    [moka同学笔记]Linux命令基本格式及目录处理命令
    [moka同学笔记]使用composer 安装yii2以及遇到的问题
    [moka同学笔记]MySql语句整理
    [moka同学笔记]三、Yii2.0课程笔记(魏曦老师教程)关联字段增加搜索
    Android笔记:ListView
    Android笔记:去除标题栏
    Android笔记:内部类
  • 原文地址:https://www.cnblogs.com/sciorz/p/9038335.html
Copyright © 2011-2022 走看看