luogu P2680 运输计划 (二分答案+树上差分)

题目背景

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

题目描述

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

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

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

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

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

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

输入输出格式

输入格式:

 

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

接下来 n-1n1 行描述航道的建设情况,其中第 ii 行包含三个整数 a_i, b_iai,bi 和 t_iti,表示第 ii 条双向航道修建在 a_iai 与 b_ibi两个星球之间,任意飞船驶过它所花费的时间为 t_iti。数据保证 1 \leq a_i,b_i \leq n1ai,bin 且 0 \leq t_i \leq 10000ti1000。

接下来 mm 行描述运输计划的情况,其中第 jj 行包含两个正整数 u_juj 和 v_jvj,表示第 jj 个运输计划是从 u_juj 号星球飞往 v_jvj号星球。数据保证 1 \leq u_i,v_i \leq n1ui,vin

 

输出格式:

 

一个整数,表示小 PP 的物流公司完成阶段性工作所需要的最短时间。

 

输入输出样例

输入样例#1: 复制
6 3 
1 2 3 
1 6 4 
3 1 7 
4 3 6 
3 5 5 
3 6 
2 5 
4 5
输出样例#1: 复制
11

说明

所有测试数据的范围和特点如下表所示

请注意常数因子带来的程序效率上的影响。

 

思路:

很明显答案是递增的,我们可以直接二分答案,设最短时间为t,那么航线时间大于t的我们就把他放到差分数组里,然后在差分数组里找到所有链公有的权值最大的边权,看减去这条边后所有航线是否都小于等于之前假设的最短时间t,.

 

这道题注意要用读入挂,要不一直TLE第13个样例。。。。

实现代码:

#include<bits/stdc++.h>
using namespace std;

const int M = 3e5 + 10;
int p[M][22],w[M],dep[M],cnt,head[M],n,ans,dist[M],num,mx,m,sum[M];
int len[M],u[M],v[M],lc[M],r,l;
struct node{
    int to,next,w;
}e[M<<1];

void Add(int u,int v,int w){
    e[++cnt].to = v;e[cnt].next = head[u];e[cnt].w = w,head[u] = cnt;
}

void dfs(int u,int fa,int deep){
    dep[u] = deep;
    p[u][0] = fa;
    for(int i = head[u];i;i=e[i].next){
        int v = e[i].to;
        if(v == fa) continue;
        dist[v] = dist[u] + e[i].w;
        w[v] = e[i].w;
        dfs(v,u,deep+1);
    }
}


void get_fa(){
    for(int j = 1;(1<<j)<=n;j++)
        for(int i = 1;i <= n;i ++)
            p[i][j] = p[p[i][j-1]][j-1];
}

int lca(int a,int b){
    if(dep[a] > dep[b]) swap(a,b);
    int h = dep[b] - dep[a];
    for(int i = 0;(1<<i)<=h;i++){
        if((1<<i)&h) b = p[b][i];
    }
    if(a != b){
        for(int i = 20;i >= 0;i --){
            if(p[a][i] != p[b][i]){
                a = p[a][i]; b = p[b][i];
            }
        }
        a = p[a][0];
    }
    return a;
}

void dfs1(int u,int fa){
    for(int i = head[u];i;i=e[i].next){
        int v = e[i].to;
        if(v == fa) continue;
        dfs1(v,u);
        sum[u] += sum[v];
    }
    if(sum[u] == num) mx = max(mx,w[u]);
}

bool check(int x){
    int k = 0;
    num = 0; mx = 0;
    memset(sum,0,sizeof(sum));
    for(int i = 1;i <= m;i ++){
        if(len[i] > x){
            sum[u[i]]++; sum[v[i]]++;
            sum[lc[i]] -= 2;
            num++;
            k = max(k,len[i]);
        }
    }
    dfs1(1,0);
    if(k - x <= mx) return 1;
    else return 0;
}

int read(){
    char c;int num,f=1;
    while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
    while(c=getchar(), isdigit(c))num=num*10+c-'0';
    return f*num;
}

inline bool read(int &num)
{
        char in;bool IsN=false;
        in=getchar();
        if(in==EOF) return false;
        while(in!='-'&&(in<'0'||in>'9')) in=getchar();
        if(in=='-'){ IsN=true;num=0;}
        else num=in-'0';
        while(in=getchar(),in>='0'&&in<='9'){
                num*=10,num+=in-'0';
        }
        if(IsN) num=-num;
        return true;
}

int main()
{
    int a,b,c;
    read(n); read(m);
    for(int i = 1;i < n;i ++){
        read(a);read(b);read(c);
        Add(a,b,c); Add(b,a,c);
    }
    dfs(1,0,1); get_fa();
    for(int i = 1;i <= m;i ++){
        read(u[i]); read(v[i]);
        lc[i] = lca(u[i],v[i]);
        len[i] = dist[u[i]] + dist[v[i]] - 2*dist[lc[i]];
        r = max(r,len[i]);
    }
    int k = 0;
    while(l <= r){
        int mid = (l + r) >> 1;
        if(check(mid)){
            k = mid;
            r = mid-1;
        }
        else l = mid+1;
    }
    printf("%d\n",k);
}

 

posted @ 2018-11-20 22:31  冥想选手  阅读(250)  评论(0编辑  收藏  举报