嘘~ 正在从服务器偷取页面 . . .

LOJ6678 礼物 题解


LOJ6678 礼物 题解

题目链接:#6678. 礼物

题意

穷愁潦倒的小 B 正在树上漫步,突然接到了小 S 的电话,要他去点 $y$ 买一些礼物,然而他现在正处于 $x$ 点,并且他身上并没有钱。

小 L 告诉了他一个绝妙的方法。树上的每个点都有大佬的存在,其中一些大佬会购买或出售(售价与收购价相同)bsoj 权限账号。而且各点 dalao 数量不同,价格也不同,这样的交易能使他赚上一笔差价。小 S 没有告诉他礼物的价钱,他只能赚尽量多的钱。由于小 S 在小 B 的身上装了追踪器,所以他只能走 $x$ 到 $y$ 的最短路,而且时间不足,因此这样的交易只能发生一次(指买一次卖一次)。

小 B 这样的大神犇显然已经知道求解方法了,但他为了不被小 S 发现,让你来帮他解决这个问题。

输入格式

输入第一行是一个整数 $n$ ,表示树上的点数。

接下来 $n$ 个正整数,表示每个点上权限号的价格。

接下来 $n-1$ 行,每行两个整数 $x, y$ ,表示第 $x$ 点和第 $y$ 点有一条边。

接下来一个整数 $m$ ,表示接下来有 $m$ 个询问。

接下来有 $m$ 行,每行两个整数 $x$ 和 $y$ ,表示小 $B$ 从第 $x$ 点出发要到第 $y$ 点。

输出格式

输出包括 $m$ 行。每行对应一个询问,一个整数,表示小 B 最多能赚到多少钱。

又是经典的“分情况跳链”,即根据链的倾斜方向选择合并方式,做了不下三道题了。

考虑树剖+线段树维护区间最大值、最小值、dfn 小到 dfn 大的答案,dfn 大到 dfn 小的答案

时间复杂度 $\mathcal{O}(n \log^2 n)$

代码:(有注释(大雾))

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(3e5 + 15))
#define ls(at) ((at) << 1)
#define rs(at) ((at) << 1 | 1)

struct Edge { int u,v,next; } e[N * 2];
struct node { int mx,mn,res1,res2; } tr[N * 4];
int n,Q,pos=1,val[N],a[N],id[N],head[N],top[N],dep[N],fa[N],sz[N],son[N];
void addEdge(int u,int v) { e[++pos] = {u,v,head[u]}; head[u] = pos; }
void dfs(int u,int f,int d)
{
    dep[u] = d; fa[u] = f; sz[u] = 1; int mx = -1;
    for(int i = head[u]; i; i = e[i].next)
    {
        int v = e[i].v; if(v == f) continue;
        dfs(v,u,d+1); sz[u] += sz[v]; if(sz[v] > mx) { mx = sz[v]; son[u] = v; }
    }
}
void dfs(int u,int ftop)
{
    static int tim = 0; top[u] = ftop; id[u] = ++tim; val[tim] = a[u];
    if(son[u]) dfs(son[u], ftop);
    for(int i = head[u]; i; i = e[i].next) {
        int v = e[i].v; if(v != fa[u] && v != son[u]) dfs(v,v);
    }
}
node merge(node l, node r)
{
    node c;
    up(c.mx = l.mx, r.mx); down(c.mn = l.mn, r.mn);
    c.res1 = max({l.res1, r.res1, r.mx - l.mn});
    c.res2 = max({l.res2, r.res2, l.mx - r.mn});
    return c;
}
void push_up(int at) { tr[at] = merge(tr[ls(at)], tr[rs(at)]); }
void build(int l,int r,int at)
{
    if(l == r) { return tr[at] = {val[l], val[l], 0, 0}, void(0); }
    int mid = (l + r) >> 1; build(l,mid,ls(at)); build(mid+1,r,rs(at)); push_up(at);
}
node query(int nl,int nr,int l = 1,int r = n,int at = 1)
{
    if(nl <= l && r <= nr) return tr[at];
    int mid = (l + r) >> 1;
    if(nl > mid) return query(nl, nr, mid + 1, r, rs(at));
    if(nr <= mid) return query(nl, nr, l, mid, ls(at));
    return merge(query(nl,nr,l,mid,ls(at)), query(nl,nr,mid+1,r,rs(at)));
}
int qRange(int x,int y)
{
    node U,V; U = V = {-INF, INF, 0, 0}; 
    while(top[x] != top[y])
    {
        if(dep[top[x]] > dep[top[y]]) {
            U = merge(query(id[top[x]], id[x]), U); x = fa[top[x]];
        }else {
            V = merge(query(id[top[y]], id[y]), V); y = fa[top[y]];
        }
    }
    // 观察 x,y 在同一重链时的倾斜方向,并且两者的 res1 是反向的
    if(dep[x] > dep[y]) {
        U = merge(query(id[y], id[x]), U); swap(U.res1, U.res2);
        return merge(U,V).res1;
    }else {
        V = merge(query(id[x], id[y]), V); swap(V.res1, V.res2);
        return merge(V,U).res2;
    }
    return -1;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1, u,v; i < n; i++) {
        cin >> u >> v; addEdge(u,v); addEdge(v,u);
    }
    cin >> Q; dfs(1,0,1); dfs(1,1); build(1,n,1);
    for(int x,y; Q--; ) {
        cin >> x >> y; cout << qRange(x,y) << '\n';
    }
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录