码迷,mamicode.com
首页 > 其他好文 > 详细

NOIP2015 运输计划

时间:2018-05-14 23:03:02      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:前缀和   use   can   工作   lin   多少   using   计划   mes   

标签: 二分 倍增 差分 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\n", ans);
    return 0;
}
/**************************************************************
    Problem: 1754
    User: upc_reserver201706
    Language: C++
    Result: 正确
    Time:916 ms
    Memory:58224 kb
****************************************************************/

NOIP2015 运输计划

标签:前缀和   use   can   工作   lin   多少   using   计划   mes   

原文地址:https://www.cnblogs.com/sciorz/p/9038335.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!