zoukankan      html  css  js  c++  java
  • 【NOIP2015】运输计划(二分,差分)

    题面

    Description

    公元 2044 年,人类进入了宇宙纪元。
    L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球。
    小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰。
    为了鼓励科技创新,L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
    在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后, 这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的 物流公司的阶段性工作就完成了。
    如果小 P 可以自由选择将哪一条航道改造成虫洞,试求出小 P 的物流公司完成阶段 性工作所需要的最短时间是多少?

    Input

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

    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

    题解

    多么好的一道题目。。。。
    既然每一条航道都是在树上跑
    显然是要求LCA的(所以我用的熟练剖分)

    把问题简化一下:
    有若干条树上的路径
    将一条边的长度变为零之后,最长距离的最小值是多少

    最长距离的最小值,想到的是二分答案

    那么,现在的问题又变成了,如何检查答案。

    对于每一个二分出来的时间
    如果某个路径的长度小于这个时间,那么这个航道做不做改动都是无所谓的
    反过来,显然就至少需要修改一条航道

    那么,如果是一条满足条件的边修改为0,那么一定存在所有的其他需要修改的航道都经过了这条边(要不然他们就不会减少了)

    所以在树上进行差分
    每一次沿着路径统计当前这条边是否可以减少
    然后判断一下减少的量够不够(就是最长的航道和当前的时间的差)
    就可以判断可行性了

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<vector>
    #include<algorithm>
    using namespace std;
    
    #define MAX 301000
    #define INF 1000000000
    
    inline int read()
    {
        register int x=0,t=1;
        register char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-'){t=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
        return x*t;
    }
    
    struct Line
    {
        int v,next,w;
    }e[MAX*3];
    
    int size[MAX],dfn[MAX],f[MAX],hson[MAX],top[MAX];
    int c[MAX],t[MAX],N,M,dis[MAX];
    int h[MAX],cnt=1,tim,line[MAX],ln[MAX],dep[MAX];
    
    inline void Add(int u,int v,int w)
    {
        e[cnt]=(Line){v,h[u],w};
        h[u]=cnt++;
    }
    
    struct Plan
    {
        int u,v,d;
    }p[MAX];
    bool operator <(Plan a,Plan b)
    {
        return a.d<b.d;
    }
    
    void DFS1(int u,int ff)
    {
        hson[u]=0;size[u]=1;f[u]=ff;dep[u]=dep[ff]+1;
        for(int i=h[u];i;i=e[i].next)
        {
            int v=e[i].v;
            if(v==ff)continue;
            dis[v]=dis[u]+e[i].w;
            ln[v]=e[i].w;
            DFS1(v,u);
            if(size[v]>size[hson[u]])hson[u]=v;
            size[u]+=size[v];
        }
    }
    
    void DFS2(int u,int tp)
    {
        top[u]=tp;dfn[u]=++tim;line[tim]=u;
        if(hson[u])DFS2(hson[u],tp);
        for(int i=h[u];i;i=e[i].next)
        {
            int v=e[i].v;
            if(v==f[u]||v==hson[u])continue;
            DFS2(v,v);
        }
    }
    
    inline int LCA(int u,int v)
    {
        int tp1=top[u],tp2=top[v];
        while(tp1!=tp2)
        {
            if(dep[tp1]<dep[tp2]){swap(tp1,tp2);swap(u,v);}
            u=f[tp1];tp1=top[u];
        }
        if(dep[u]<dep[v])swap(u,v);
        return v;
    }
    
    inline void Count(int u,int v)
    {
        int tp1=top[u],tp2=top[v];
        while(tp1!=tp2)
        {
            if(dep[tp1]<dep[tp2]){swap(tp1,tp2);swap(u,v);}
            c[dfn[tp1]]++;c[dfn[u]+1]--;
            u=f[tp1];tp1=top[u];
        }
        if(dep[u]<dep[v])swap(u,v);
        c[dfn[u]+1]--;c[dfn[v]+1]++;
    }
    
    inline bool Check(int tt)
    {
        int sum=0;
        memset(c,0,sizeof(c));
        if(p[M].d<=tt)return true;
        for(int i=M;i;--i)
        {
            if(p[i].d<=tt)break;
            sum++;
            Count(p[i].u,p[i].v);
        }
        int ss=0;
        for(int i=1;i<=N;++i)
        {
            ss+=c[i];
            if(ss==sum)
            {
                if(p[M].d-ln[line[i]]<=tt)return true;
            }
        }
        return false;
    }
    
    int main()
    {
        N=read();M=read();
        for(int i=1;i<N;++i)
        {
            int u=read(),v=read(),w=read();
            Add(u,v,w);Add(v,u,w);
        }
        
        DFS1(1,0);DFS2(1,1);
        
        for(int i=1;i<=M;++i)
        {
            p[i].u=read();p[i].v=read();
            p[i].d=dis[p[i].u]+dis[p[i].v]-2*dis[LCA(p[i].u,p[i].v)];
        }
        
        sort(&p[1],&p[M+1]);
        
        int l=0,r=INF;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(Check(mid))r=mid;
            else l=mid+1;
        }
        
        printf("%d
    ",l);
        
        return 0;
    }
    
    
  • 相关阅读:
    POJ 1936 All in All
    Blue Jeans POJ 3080 寻找多个串的最长相同子串
    Spell checker POJ 1035 字符串
    密码锁
    luogu P1083 借教室
    BZOJ 1588: [HNOI2002]营业额统计
    BZOJ 1433: [ZJOI2009]假期的宿舍
    luogu P1231 教辅的组成
    luogu P2756 飞行员配对方案问题
    luogu P3386 【模板】二分图匹配
  • 原文地址:https://www.cnblogs.com/cjyyb/p/7531775.html
Copyright © 2011-2022 走看看