[NOIP2015]运输计划(树上差分+LCA+二分)

news/2024/7/6 4:43:00

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 Format

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

接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1≤ai,bi≤n 且 0≤ti≤1000。

接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1≤ui,vi≤n

Output Format

输出文件只包含一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。

n,m<=300000

Solution

先分析一下题目,n个点n-1条边且图联通,明显是树结构,

不难发现,题目是求最大值最小,不妨用二分答案

那么如何验证呢,对于每个计划,检查是否存在一些不合法线路,即两点间最短距离>mid,那么显然我们要选一条边作为虫洞,而且是所有不合法线路都经过的边

我们开一个数组sum[i]表示编号为i的边被多少条线路覆盖,那么对于所有不合法线路都经过的边,找一个最长的一条,把它设为虫洞,为最优

那么关键在于如何维护数组sum

经过shawn_xc学长的指导,用树上差分可以解决,

当发现一条不合法路径u to v时,令sum[u]++,sum[v]++,sum[lca(u,v)]-=2,即树上差分

最后就判断一下满足即可被所以不合法路线经过的边中最长的边设为虫洞后是否可行即可

Code

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define N 300010
using namespace std;

struct plan
{
    int u, v, lca, dis;
} node[N];
struct info {
    int to, nex, w;
} e[N * 2];
int n, m, head[N * 2], tot;
int l, r, edge[N];
bool vis[N];
int _log, fa[N][19], dep[N], dis[N];

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

inline void add_edge(int u, int v, int w) {
    e[++tot].to = v;
    e[tot].w = w;
    e[tot].nex = head[u];
    head[u] = tot;
}

void dfs(int u) {
    vis[u] = 1;

    for (int i = 1; i <= _log; ++i)
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (int i = head[u]; i; i = e[i].nex) {
        int v = e[i].to;
        if (vis[v]) continue;
        edge[v] = i;
        dep[v] = dep[u] + 1;
        fa[v][0] = u;
        dis[v] = dis[u] + e[i].w;
        dfs(v);
    }
}

int LCA(int u, int v) {
    if (dep[u] > dep[v]) swap(u, v);
    int d = dep[v] - dep[u];

    for (int i = 0; i <= _log; ++i)
        if (d & (1 << i))
            v = fa[v][i];

    if (u == v) return u;

    for (int i = _log; i >= 0; i--)
        if (fa[u][i] != fa[v][i]) {
            u = fa[u][i];
            v = fa[v][i];
        }

    return fa[u][0];
}

int sum[N];
void update(int u, int fa) {
    for (int i = head[u]; i; i = e[i].nex) {
        int v = e[i].to;
        if (v == fa) continue;
        update(v, u);
        sum[u] += sum[v];
    }
}

bool pd(int mid) {
    int tot = 0, dec = 0;
    memset(sum, 0, sizeof(sum));
    for (int i = 1; i <= m; ++i)
        if (node[i].dis > mid) {
            tot++;
            dec = max(dec, node[i].dis - mid);
            sum[node[i].u]++;
            sum[node[i].v]++;
            sum[node[i].lca] -= 2;
        }
    update(1, 1);
    for (int i = 1; i <= n; ++i)
        if (sum[i] == tot && e[edge[i]].w >= dec) return 1;
    return 0;
}

int main() {
    n = read(), m = read();
    _log = log(n) / log(2);
    for (int i = 1; i < n; ++i) {
        int u = read(), v = read(), w = read();
        add_edge(u, v, w);
        add_edge(v, u, w);
    }
    dfs(1);

    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read();
        node[i].u = u, node[i].v = v;
        node[i].lca = LCA(u, v);
        node[i].dis = (dis[u] + dis[v]) - 2 * dis[node[i].lca];
        r = max(r, node[i].dis);
    }
    int Ans;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (pd(mid))
            Ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    printf("%d\n", Ans);
    return 0;
}

转载于:https://www.cnblogs.com/void-f/p/7677567.html


http://www.niftyadmin.cn/n/4637503.html

相关文章

android debug bridge(adb),ADB(Android Debug Bridge)的常用命令以及使用

adb devices 用于查看adb链接的设备、adb start-service 启动设备adb kill-service 停止设备adb reboot 重启设备adb version 查看adb版本号adb pull 用来将移动端设备 的文件复制到PC端adb push 用…

android:completionthreshold=1,Android笔试95题讲述.doc

内测/月考类型&#xff1a;(笔试)范围&#xff1a;内测/月考日期&#xff1a;时长&#xff1a;1小时总分数&#xff1a;100 分姓名&#xff1a;准考证号&#xff1a;证件号码&#xff1a;理论部分注意&#xff1a;考试结束试卷必须交回&#xff0c;答案填写在答题卡上1、下面关…

在线人数HTML源码,PHP网站在线人数

刚用PHP写了一个网站在线人数的程序&#xff0c;请大家进来指点下&#xff01;我是用PHPMYSQL来写的&#xff0c;原理&#xff1a;网站在线人数的程序代码后台有MYSQL数据库支持&#xff0c;可以直接统计出网站当前的在线人数。首先我创建MYSQL数据库表。CREATE TABLE tablenam…

清华镜像站 android,清华 AOSP 镜像源配置笔记

在使用清华的镜像源配置下载AOSP时&#xff0c;有些步骤官网写的比较模糊&#xff0c;摸索过程浪费了一些时间&#xff0c;记一个笔记&#xff0c;以供之后避坑。下载 repo 工具此处与官网一致&#xff0c;原文抄录。mkdir ~/binPATH~/bin:$PATHcurl https://storage.googleapi…

html 实时曲线 js,javascript – 滑块移动时的html5-canvas绘图曲线

嗨,我想在移动滑块时绘制曲线.我有两种曲线.贝塞尔曲线和二次曲线.当我绘制曲线时,我想在同一条路径上移动一个对象.我希望它是动态的,所以如果我能够改变曲线点.所以我需要一个函数,我可以调用滑块更改,因为我正在为行做.这是我的代码.此代码仅适用于chrome..wrapper {margin:…

html编写一个四则运算函数,加减乘除四则运算(javascript版)

加减乘除四则运算(javascript)1、调用方法&#xff1a;var str "134-5-6*7/89-10/11"; //具体的计算公式calcArith(str); //调用函数2、函数实现&#xff1a;//主调用函数function calcArith(strV) {if (("-*/").indexOf(strV.substring(0,1)) > 0) {s…

控制计算机桌面图标,(1)在桌面上显示“计算机”“控制面板”图标,然后隐藏“控制面板”图标。...

【单选题】( )。【单选题】即期外汇业务中汇率最高的是( )【单选题】下列说法正确的是【填空题】V型从V型从V型,____。【判断题】氢质子所处的化学环境对MR信号无影响。【问答题】请列出1949年新中国成立以来曾入选我国国家男、女篮球队各5~8名当时较著名核心后卫的名字。【多选…

计算机科学与技术考研难度排行,考研专业的难度排名

根据调查&#xff0c;考研难度系数由高到低排名依次有&#xff1a;医学、工程力学、会计、金融、法律、电子科学与技术专业、心理学、新闻传播学、计算机科学与技术、翻译&#xff0c;这些专业被列为最难考的专业。考研专业难度系数排行一、医学&#xff1a;医学专业毕业生可谓…