BZOJ 1935 NOIP 2015 运输计划 transport

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。数据保证 1≤ai,bi≤n 且 0≤ti≤1000。接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1≤ui,vi≤n

Output

输出文件只包含一个整数,表示小 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`

HINT

将第 1 条航道改造成虫洞: 则三个计划耗时分别为:11,12,11,故需要花费的时间为 12。

将第 2 条航道改造成虫洞: 则三个计划耗时分别为:7,15,11,故需要花费的时间为 15。

将第 3 条航道改造成虫洞: 则三个计划耗时分别为:4,8,11,故需要花费的时间为 11。

将第 4 条航道改造成虫洞: 则三个计划耗时分别为:11,15,5,故需要花费的时间为 15。

将第 5 条航道改造成虫洞: 则三个计划耗时分别为:11,10,6,故需要花费的时间为 11。

故将第 3 条或第 5 条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为 11。

Solution

这道题我们用树链剖分做。

思路大致是这样的:

首先题目要求最大值中最小的那个,考虑二分,

那么问题来了, \(check(mid)\) 怎么个流程呢?

我们在所有的路径中,找出\(len\)大于\(mid\)的路径,对这些路径在树上取一个交集\(M\),

然后在\(M\)中找到长度最大的那条边\(e\),然后看看路径中最长的那条减去边\(e\)的长度和\(mid\)大小就可以了。

怎么取交集呢?

我们可以先进行一遍树链剖分,用一遍差分就可以了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void sol(int u, int v) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        d[tid[top[u]]]++;
        d[tid[u]+1]--;
        u = fat[top[u]];
    }
    if (dep[u] < dep[v]) swap(u, v);
    d[tid[v]+1]++;
    d[tid[u]+1]--;
    return;
}

差分代码,注意细节,第一个差分

d[tid[top[u]]]++;

其中并没有加一。

这是为什么呢? 如图

看左边的那一支和右边的那一支,求路径的时候有中间那条轻链。

因此上边的那个玩意不用加。

下面是代码:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <cstdio>
#include <algorithm>
int getint() {
    int r = 0, k = 1;
    char c;
    for ( c = getchar(); c < '0' || c > '9'; c = getchar()) if (c == '-') k = -1;
    for ( ;'0' <= c && c <= '9'; c = getchar()) r = r * 10 - '0' + c;
    return r * k;
}
const int maxn = 300005;
int n, m;
struct edge_type {
    int to, next, val;
} edge[maxn << 1];
struct plan_type {
    int x, y, len;
} plan[maxn];
int h[maxn], cnte = 0;
void ins(int u, int v, int w) {
    edge[++cnte].to = v;
    edge[cnte].next = h[u];
    edge[cnte].val = w;
    h[u] = cnte;
}
int fat[maxn], top[maxn], tid[maxn], tmp[maxn], cos[maxn], dep[maxn], dis[maxn], siz[maxn], son[maxn];
void fhe(int x, int father, int depth, int nd) {
    dep[x] = depth;
    dis[x] = nd;
    fat[x] = father;
    siz[x] = 1; son[x] = 0;
    for (int i = h[x]; i; i = edge[i].next) {
        if (edge[i].to == father) continue;
        tmp[edge[i].to] = edge[i].val;
        fhe(edge[i].to, x, depth + 1, nd + edge[i].val);
        siz[x] += siz[edge[i].to];
        if (son[x] == 0 || siz[son[x]] < siz[edge[i].to])
            son[x] = edge[i].to;
    }
}
int dfs_clock = 0;
void che(int x, int tp) {
    top[x] = tp; tid[x] = ++dfs_clock; cos[dfs_clock] = tmp[x];
    if (son[x]) {
        che(son[x], tp);
        for (int i = h[x]; i; i = edge[i].next) {
            if (edge[i].to == fat[x] || edge[i].to == son[x]) continue;
            che(edge[i].to, edge[i].to);
        }
    }
}
int TTT;
void swap(int &a, int &b) {
    TTT = a; a = b; b = TTT;
}
int lca(int a, int b) {
    while (top[a] != top[b]) {
        if (dep[top[a]] < dep[top[b]]) swap(a, b);
        a = fat[top[a]];
    }
    if (dep[a] < dep[b]) return a;
    return b;
}
bool cmp(plan_type a, plan_type b) {
    return a.len < b.len;
}
int q[maxn], cfsz[maxn];
int max(int a, int b) {
    return a > b ? a : b;
}
void cf(int u, int v) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        cfsz[tid[top[u]]]++;
        cfsz[tid[u]+1]--;
        u = fat[top[u]];
    }
    if (dep[u] < dep[v]) swap(u, v);
    cfsz[tid[v]+1]++;
    cfsz[tid[u]+1]--;
    return;
}
int max_plan_len = 0;
bool check(int x) {
    int i, j, sz;
    for (i = 0; i < m; ++i) if (plan[i].len > x) break;
    sz = m - i;
    if (q[sz] != 0) return max_plan_len <= x + q[sz];
    for (j = 1; j <= n; ++j) cfsz[j] = 0;
    for (j = i; j < m; ++j) cf(plan[j].x, plan[j].y);
    int T = 0;
    for (j = 1; j <= n; ++j) {
        T += cfsz[j];
        if (T == sz) q[sz] = max(q[sz], cos[j]);
    }
    return max_plan_len <= x + q[sz];
}
int main () {
    n = getint(); m = getint();
    int u, v, w;
    for (int i = 1; i < n; ++i) {
        u = getint(); v = getint(); w = getint();
        ins(u, v, w); ins(v, u, w);
    }
    fhe(1, 0, 0, 0);
    che(1, 1);
    for (int i = 0; i < m; ++i) {
        u = getint(); v = getint();
        plan[i].x = u; plan[i].y = v;
        plan[i].len = dis[u] + dis[v] - (dis[lca(u, v)] << 1);
        max_plan_len = max(plan[i].len, max_plan_len);
    }
    std::sort(plan, plan + m, cmp);
    int l = 0, r = max_plan_len, mid;
    while (l < r) {
        mid = (l + r) >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%d\n", r);
    return 0;
}