题解 P2680 【运输计划】

· · 题解

智商不够,数据结构来凑QwQ

这里提供一种比较暴力的做法

因为码量较大,适合对线段树,树状数组,树剖熟悉的同学做

直观地理解题目:

对于最暴力的做法:

对于上面这种暴力做法,肯定会T,我们先解决几个问题并想想怎么优化:

如何求树上路径的值?

我们必须要试全部的n-1条边嘛?

我们每次都要重新统计嘛?

这样我们就把问题都解决了,所以解题步骤是:

=====

最后再强调一下需要注意的问题:

CODE:

(因为方法几乎都是数据结构,而且步骤已经讲的非常清楚了,所以注释只标个大方向,就不每行解释了,而且代码量也挺大)

#pragma warning (disable:4996)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define RG register
#define mid ((x+y)>>1)
#define lson (pst<<1)
#define rson (pst<<1|1)
using namespace std;
const int maxn = 3e5 + 5, maxm = maxn << 1, inf = 0x7fffffff;
int x[maxn], y[maxn], z[maxn], p[maxn];//x,y,z为每条边的数据,p[x]为x代表边的权值
int head[maxm], nxt[maxm], v[maxm], cnt;//前向星
int son[maxn], dad[maxn], sz[maxn], depth[maxn], root;//树剖dfs1
int id[maxn], top[maxn], rak[maxn], num;//树剖dfs2
int c[maxn], d[maxn], srt[maxn];//记录区间并排序的数组
int ma, mb, mc;//最大路径的记录
int n, m;

struct Binary_Indexed_Tree//求和树状数组
{
    int a[maxn];

    inline int lowbit(int k) { return k & (-k); }
    inline void update(int x, int k) { for (int i = x; i <= n; i += lowbit(i))  a[i] += k; }
    inline int query(int x) { int i = x, ans = 0; for (i = x; i >= 1; i -= lowbit(i))   ans += a[i]; return ans; }
    inline void build(int x) { for (int i = 1; i <= n; i++) update(i, p[rak[i]]); }
    inline int sum(int l, int r) { return query(r) - query(l - 1); }
}BIT;

inline int max(int x, int y) { return x > y ? x : y; }
inline int min(int x, int y) { return x < y ? x : y; }

struct Segment_Tree//最大值线段树
{
    int mx[maxn << 2], tag[maxn << 2];

    inline void pushdown(int pst)
    {
        if (!tag[pst])  return;
        int k = tag[pst];
        mx[lson] = max(mx[lson], k), mx[rson] = max(mx[rson], k);
        tag[lson] = max(tag[lson], k), tag[rson] = max(tag[rson], k);
        tag[pst] = 0; return;
    }

    inline void pushup(int pst) { mx[pst] = max(mx[lson], mx[rson]); }

    inline void update(int x, int y, int pst, int l, int r, int k)
    {
        if (x > y || y<l || x>r)    return;
        if (l <= x && y <= r) { mx[pst] = max(mx[pst], k), tag[pst] = max(tag[pst], k); return; }
        pushdown(pst);
        update(x, mid, lson, l, r, k), update(mid + 1, y, rson, l, r, k);
        pushup(pst); return;
    }

    inline int query(int x, int y, int pst, int p)
    {
        if (x == y) return mx[pst];
        pushdown(pst);
        if (p <= mid)   return query(x, mid, lson, p);
        else return query(mid + 1, y, rson, p);
    }
}ST;

inline void addline(int x, int y) { v[cnt] = y, nxt[cnt] = head[x], head[x] = cnt++; }

inline int read()
{
    RG char c = getchar(); RG int x = 0;
    while (c<'0' || c>'9')  c = getchar();
    while (c >= '0'&&c <= '9')  x = (x << 3) + (x << 1) + c - '0', c = getchar();
    return x;
}

inline void dfs1(int x, int f, int d)//树剖
{
    dad[x] = f, depth[x] = d, sz[x] = 1;
    for (RG int i = head[x]; ~i; i = nxt[i])
    {
        if (v[i] == f)  continue;
        dfs1(v[i], x, d + 1);
        sz[x] += sz[v[i]];
        if (sz[v[i]] > sz[son[x]])  son[x] = v[i];
    }
    return;
}

inline void dfs2(int x, int t)//树剖
{
    top[x] = t, id[x] = ++num, rak[id[x]] = x;
    if (!son[x])    return;
    dfs2(son[x], t);
    for (RG int i = head[x]; ~i; i = nxt[i])
        if (v[i] != dad[x] && v[i] != son[x])   dfs2(v[i], v[i]);
    return;
}

inline int sum(int x, int y)//求某条路径的权值
{
    RG int tx = top[x], ty = top[y], ans = 0;
    while (tx != ty)
    {
        if (depth[tx] >= depth[ty]) ans += BIT.sum(id[tx], id[x]), x = dad[tx], tx = top[x];
        else ans += BIT.sum(id[ty], id[y]), y = dad[ty], ty = top[y];
    }
    if (id[x] <= id[y]) ans += BIT.sum(id[x] + 1, id[y]);
    else ans += BIT.sum(id[y] + 1, id[x]);
    return ans;
}

inline bool cmp(int x, int y) { return c[x] < c[y]; }

inline void update(int x, int y, int z)//更新mx数组(其实是更新最大值线段树)
{
    RG int tx = top[x], ty = top[y], t = 0;
    while (tx != ty)
    {
        if (depth[tx] >= depth[ty]) c[++t] = id[tx], d[t] = id[x], x = dad[tx], tx = top[x];
        else c[++t] = id[ty], d[t] = id[y], y = dad[ty], ty = top[y];
    }
    if (id[x] <= id[y]) c[++t] = id[x] + 1, d[t] = id[y];
    else c[++t] = id[y] + 1, d[t] = id[x];
    for (int i = 1; i <= t; i++)    srt[i] = i;
    sort(srt + 1, srt + t + 1, cmp);
    if (c[srt[1]] > 1)  ST.update(1, n, 1, 1, c[srt[1]] - 1, z);
    if (d[srt[t]] < n)  ST.update(1, n, 1, d[srt[t]] + 1, n, z);
    for (int i = 1; i < t; i++) ST.update(1, n, 1, d[srt[i]] + 1, c[srt[i + 1]] - 1, z);
    return;
}

inline int find_ans(int x, int y)//在最大路径上遍历并清零边求答案
{
    RG int ans = inf;
    if (x == y) return 0;
    if (depth[x] < depth[y])    swap(x, y);
    while (depth[x] != depth[y])    ans = min(ans, max(mc - p[x], ST.query(1, n, 1, id[x]))), x = dad[x];
    while (x != y)
    {
        if (depth[x] > depth[y])    ans = min(ans, max(mc - p[x], ST.query(1, n, 1, id[x]))), x = dad[x];
        else ans = min(ans, max(mc - p[y], ST.query(1, n, 1, id[y]))), y = dad[y];
    }
    return ans;
}

int main(void)
{
    memset(head, -1, sizeof(head));
    n = read(), m = read();
    for (int i = 1; i < n; i++) x[i] = read(), y[i] = read(), z[i] = read();
    for (int i = 1; i < n; i++) addline(x[i], y[i]), addline(y[i], x[i]);
    root = rand() % n + 1, dfs1(root, 0, 1), dfs2(root, root);//树剖
    for (int i = 1; i < n; i++)
    {
        if (depth[x[i]] > depth[y[i]])  p[x[i]] = z[i];//把深度大的点作为一条边的代表
        else p[y[i]] = z[i];
    }
    BIT.build(n);
    for (int i = 1; i <= m; i++)
    {
        RG int a = read(), b = read(), temp;
        temp = sum(a, b), update(a, b, temp);
        if (temp >= mc) ma = a, mb = b, mc = temp;//求最大路径
    }
    printf("%d\n", find_ans(ma, mb));
    return 0;
}

真的很用心写的一篇题解,留个赞再走吧QwQ