P3979 遥远的国度 题解

发布时间 2023-07-29 17:52:02作者: MoyouSayuki

P3979 遥远的国度

题意

一棵树,\(n\le 10^5\),三个操作,\(m\le 10^5\),点带权。

  1. 换根
  2. 路径推平
  3. 子树查最小值

思路

如果没有换根,操作 2, 3 是裸的树剖,考虑换根后的询问如何处理。

显然不能再做一遍树剖,只能假装我们换根了,询问可以分成四种情况,令原根为 \(root\),新根为 \(id\),查询点为 \(x\)

  1. \(x = id\),全局最小值;
  2. \(x\)\(id\) 的子树内,原子树最小值
  3. \(x\)\(root\)\(id\) 的路径上,即 \(id\)\(x\) 的原子树内,则询问等价于除了以 \(id\) 所在的 \(x\) 的子树中所有点的查询。
  4. \(x\)\(id\) 不在同一颗 \(root\) 的子树内, 原子树最小值。

最棘手的是情况 3,如果 \(\min\) 的贡献可以消除,只需要用全局贡献减去子树贡献,然而 \(\min\) 操作的属性并不支持贡献的消除,所以只能反向考虑问题,由于子树内的点的编号是连续的,所以可以考虑合并左半部分的贡献与右半部分的贡献。

如果 \(LCA(x, id) = x\),则 \(id\)\(x\) 的子树内

// Problem: P3979 遥远的国度
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-07-29 13:47:25

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#define int long long
#define inf (int)2e18
using namespace std;
const int N = 1e5 + 10;

int n, m, temp[N];
vector<int> g[N];

int sz[N], hson[N], dfn[N], id[N], top[N], fa[N], w[N], tmst, dep[N];
void dfs1(int u, int fat)
{
    fa[u] = fat, sz[u] = 1, dep[u] = dep[fat] + 1;
    for(auto v : g[u])
    {
        if(v == fa[u]) continue;
        dfs1(v, u);
        sz[u] += sz[v];
        if(sz[v] > sz[hson[u]])
            hson[u] = v;
    }
}

void dfs2(int u, int anc)
{
    dfn[++ tmst] = u, id[u] = tmst, w[tmst] = temp[u], top[u] = anc; 
    if(!hson[u]) return ;
    dfs2(hson[u], anc);
    for(auto v : g[u])
    {
        if(v == fa[u] || v == hson[u]) continue;
        dfs2(v, v);
    }
}

// The code below implemented a Segment Tree

struct qwq
{
    int l, r, dat, tag;
} tr[N << 2];

void up(int u)
{
    tr[u].dat = min(tr[u << 1].dat, tr[u << 1 | 1].dat);
}

void down(int u)
{
    if(tr[u].tag == 0) return ;
    tr[u << 1].dat = tr[u << 1 | 1].dat = tr[u].tag;
    tr[u << 1].tag = tr[u << 1 | 1].tag = tr[u].tag;
    tr[u].tag = 0;
}

void build(int u, int l, int r)
{
    tr[u] = {l, r, inf, 0};
    if(l == r) tr[u].dat = w[l], tr[u].tag = w[l]; // TODO
    else build(u << 1, l, l + r >> 1), build(u << 1 | 1, (l + r >> 1) + 1, r), up(u);
}

void update(int u, int ql, int qr, int v)
{
    int l = tr[u].l, r = tr[u].r, mid = l + r >> 1;
    if (ql <= l && qr >= r)
    {
        tr[u].dat = v, tr[u].tag = v;
        return;
    }
    down(u);
    if (ql <= mid) update(u << 1, ql, qr, v);
    if (qr > mid)  update(u << 1 | 1, ql, qr, v);
    up(u);
}

int query(int u, int ql, int qr)
{
    int l = tr[u].l, r = tr[u].r, mid = l + r >> 1;
    if (ql <= l && qr >= r)
        return tr[u].dat;
    down(u);
    int tmp = inf;
    if (ql <= mid) tmp = query(u << 1, ql, qr);
    if (qr > mid)  tmp = min(tmp, query(u << 1 | 1, ql, qr));
    up(u);
    return tmp;
}

int LCA(int a, int b)
{
    while(top[a] != top[b])
    {
        if(dep[top[a]] < dep[top[b]]) b = fa[top[b]];
        else a = fa[top[a]];
    }
    return (dep[a] < dep[b] ? a : b);
}

void HLDupdate(int a, int b, int v)
{
    while(top[a] != top[b])
    {
        if(dep[top[a]] < dep[top[b]]) swap(a, b);
        update(1, id[top[a]], id[a], v);
        a = fa[top[a]];
    }
    if(dep[top[a]] > dep[top[b]]) swap(a, b);
    update(1, id[a], id[b], v);
}

int jump(int x, int y)
{
    while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]]) swap(x, y);
        if(fa[top[x]] == y) return top[x];
        x = fa[top[x]];
    }
    return (dep[x] < dep[y] ? hson[x] : hson[y]);
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    n = read(), m = read();
    for(int i = 1, a, b; i < n; i ++)
        a = read(), b = read(), g[a].push_back(b), g[b].push_back(a);
    for(int i = 1; i <= n; i ++)
        temp[i] = read();
    dfs1(1, 1), dfs2(1, 1);
    build(1, 1, n);
    int root = read();
    while(m --)
    {
        int op, a, b, c;
        op = read();
        if(op == 1)
            root = read();
        else if(op == 2)
        {
            a = read(), b = read(), c = read();
            HLDupdate(a, b, c);
        }
        else
        {
            int x = read();
            if(root == x)
                cout << query(1, 1, n) << '\n';
            else
            {
                int y = jump(root, x);
                if(LCA(x, root) == x)
                    cout << min(query(1, 1, id[y] - 1), (id[y] + sz[y] <= n ? query(1, id[y] + sz[y], n) : inf)) << '\n';
                else 
                    cout << query(1, id[x], id[x] + sz[x] - 1) << '\n';
            }
        }
    }

    return 0;
}