题目描述

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条树中的路径。允许将一条树边的权改为0,并使得这些路径的最长距离最小。输出该修改方案中这些路径中最长的一条。
注意最长的路径最小,我们就想到了二分答案。
那么怎么检查答案呢?
首先,我们要二分这些路径中的最长路径,设为k
如果有某一路径的距离大于k,那么我们显然就要修改这个路径中的边。就把这些路径标记起来,然后找一条都覆盖的边。如果有某一条边能满足边权值权值 >= 最长路径-k,那么这个答案k是可行的。
那么怎么要找出来这些覆盖的边呢?
当树退化为一条链时,当然是差分好了。
对于我们的路径i,设起点a[i]b[i]l[i] = LCA(a[i],b[i])那么我们就可以让差分数组f[a[i]]++,f[b[i]]++,f[l[i]] -= 2
那么最后,数据中提到了注意常数因子的影响
那么我们要注意以下几点:
1. 尽量使用快读
2. 用Vector的同学改为邻接表或前向星
3. 缩小二分范围
4. Tarjan好像更快(我没试过)

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>

const int MAXN = 300000 + 5;
const int logN = 24;

inline void Read(int &x){
    x = 0;
    int flag = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-') flag = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
    }
    x *= flag;
}

struct Edge{
    int to,w,next;
}e[MAXN * 2];

int Max = 0;

int head[MAXN],cnt;
int N,M;
int f[MAXN][30 + 5],deep[MAXN],dist[MAXN],topre[MAXN],p[MAXN];

inline void add(int u,int v,int w){
    e[cnt] = Edge{v,w,head[u]};
    head[u] = cnt++;
    e[cnt] = Edge{u,w,head[v]};
    head[v] = cnt++;
}

void dfs(int v,int fa){
    p[++cnt] = v;
    for(int i = 1;i < logN;i++){
        f[v][i] = f[f[v][i-1]][i-1];
        if(!f[v][i]) break;
    }
    for(int i = head[v];i;i = e[i].next){
        if(e[i].to != fa){
            dist[e[i].to] = dist[v] + e[i].w;
            deep[e[i].to] = deep[v] + 1;
            f[e[i].to][0] = v;
            topre[e[i].to] = e[i].w;
            dfs(e[i].to,v);
        }
    }
}

int lca(int x,int y){
    if(deep[x] > deep[y])
        std::swap(x,y);
    for(int i = logN-1;i >= 0;i--)
        if(deep[x] <= deep[f[y][i]])
            y = f[y][i];
    for(int i = logN-1;i >= 0;i--){
        if(f[x][i] != f[y][i]){
            x = f[x][i];
            y = f[y][i];
        }
    }
    if(x != y) x = f[x][0];
    return x;
}
int a[MAXN],b[MAXN],l[MAXN],d[MAXN],cf[MAXN];
bool checker(int k){
    memset(cf,0,sizeof(cf));
    cnt = 0;
    for(int i = 1;i <= M;i++){
        if(d[i] > k){
            cf[a[i]]++;
            cf[b[i]]++;
            cf[l[i]]-=2;
            cnt++;
        }
    }
    for(int i = N;i >= 1;i--){
        cf[f[p[i]][0]] += cf[p[i]];
        if(topre[p[i]] >= Max - k && cf[p[i]] == cnt) return true;
    }
    return false;
}

int Binary_Search(int L,int R){
    int mid;

    while(L < R){
        mid = (L + R) >> 1;
        if(checker(mid))
            R = mid;
        else L = mid + 1;
    }
    return L;
}

int main(){
    Read(N);Read(M);
    int Max1 = 0;
    for(int u,v,w,i = 1;i < N;i++){
        Read(u);Read(v);Read(w);
        add(u,v,w);
        Max1 = std::max(w,Max1);
    }
    cnt = 0;
    deep[1] = 1;deep[0] = -1;
    dfs(1,0);
    for(int i = 1;i <= M;i++){
        Read(a[i]);Read(b[i]);
        l[i] = lca(a[i],b[i]);
        d[i] = dist[a[i]] + dist[b[i]] - (dist[l[i]] << 1);
        Max = std::max(Max,d[i]);
    }
    int ans = Binary_Search(Max - Max1, Max + 1);
    printf("%d\n",ans);
    return 0;
}

一个OIer。