运输计划(二分,树上差分)

本文介绍了一种通过树上差分算法解决网络中给定树结构的运输问题。通过二分查找策略,确定能将边耗时变为0以满足最多超时需求的最小时间,利用差分法找出关键边并检查是否能使所有超时路径时间缩短。关键代码展示了如何使用DFS和树上差分来实现这一过程。
摘要由CSDN通过智能技术生成

运输计划

题目链接

题目大意:给出一颗有 n n n 个点的树,和走完某条边的耗时。现有 m m m 个从点 u u u 到点 v v v 的需求,且我们可以把某一条边的耗时变成 0 0 0 ,问最短的最长耗时是多少。

我们可以考虑二分答案,那问题就是我们怎样进行 c h e c k check check 。我们先把路径长度超过 m i d mid mid 的需求记录下来,然后我们就是要找到一条边,被所有这些超时的需求的路径经过,且这个边尽可能长。为什么要找到所有超时需求都经过的边呢?因为如果我们找到的边不是所有超时需求( > m i d > mid >mid 的路径)都经过的,那我把其变成 0 0 0 ,只是把部分超时的需求的时间减少了一些,另外一些没经过这条边的总时间根本没有变化,也就是必定依然会存在超时的路径。这个路径要尽量长就不必多解释了,那么 c h e c k check check 是否成功,就看耗时最长超时路径减去我们找出来的符合条件的最长边是不是 ≤ m i d ≤ mid mid 了。那么这里找到所有超时路径都经过的边就靠树上差分解决,树上差分可以得知每条边被经过多少次,那么这个次数等于超时需求个数,就代表这条边被所有超时需求经过。

c o d e code code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 3e5 + 2;

struct Edge {
    int v;
    ll val;
};
struct k {
    int u;
    int v;
    int l;
    ll dis;
}q[N];
int n, m, flag, sum;
int father[N], depth[N], size[N], son[N], top[N], cnt, back[N], t[N];
int tmp[N];
ll Max, mid;
vector<Edge> g[N];

void dfs_1(int u, int d, int b, int fa, ll tm) {
    depth[u] = d;
    size[u] = 1;
    father[u] =  fa;
    back[u] = b;
    t[u] = tm;
    for (int i = 0; i < (int)g[u].size(); ++i) {
        int v = g[u][i].v;
        if (v != fa) {
            dfs_1(v, d + 1, g[u][i].val, u, tm + g[u][i].val);
            size[u] += size[v];
            if (size[v] > size[son[u]]) {
                son[u] = v;
            }
        }
    }
}

void dfs_2(int u, int t) {
    top[u] = t;
    if (!son[u]) return ;
    dfs_2(son[u], t);
    for (int i = 0; i < (int)g[u].size(); ++i) {
        int v = g[u][i].v;
        if (v != son[u] && v != father[u]) {
            dfs_2(v, v);
        }
    }
}

void dfs_3(int u) {
    for (int i = 0; i < (int)g[u].size(); ++i) {
        int v = g[u][i].v;
        if (v != father[u]) {
            dfs_3(v);
            tmp[u] += tmp[v];
        }
    }
    if (tmp[u] == sum && Max - back[u] <= mid) {
        flag = 1;
    }
}

bool check() {
    memset(tmp, 0, sizeof tmp);
    Max = -1;
    flag = 0;
    sum = 0;
    for (int i = 0; i < m; ++i) {
        if (q[i].dis > mid) {
            sum++;
            Max = max(Max, q[i].dis);
            tmp[q[i].u]++; tmp[q[i].v]++; tmp[q[i].l] -= 2;
        }
    }
    if (Max == -1ll) return true;
    dfs_3(1);
    if (flag) return true;
    return false;
}

int lca(int x, int y) {
    while(top[x] ^ top[y]) {
        if (depth[top[x]] < depth[top[y]]) swap(x, y);
        x = father[top[x]];
    }
    return depth[x] < depth[y] ? x : y;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n - 1; ++i) {
        int u, v;
        ll val;
        scanf("%d%d%lld", &u, &v, &val);
        g[u].push_back({v, val});
        g[v].push_back({u, val});
    }
    dfs_1(1, 0, 0, 0, 0);
    dfs_2(1, 1);
    for (int i = 0; i < m; ++i) {
        scanf("%d%d", &q[i].u, &q[i].v);
        q[i].l = lca(q[i].u, q[i].v);
        q[i].dis = t[q[i].u] + t[q[i].v] - 2 * t[q[i].l];
    }
    ll l = 0, r = 1e11;
    while(l <= r) {
        mid = l + r >> 1ll;
        if (check()) r = mid - 1;
        else l = mid + 1;
    }
    printf("%lld", r + 1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值