【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\n",l);
    
    return 0;
}

posted @ 2017-09-16 16:51  小蒟蒻yyb  阅读(509)  评论(0编辑  收藏  举报