P3979 遥远的国度
题目大意
一棵 个节点的树,一开始给定了初始的根,每个节点上有权值 。 次操作,共 种操作类型:
:将 作为新的根
:将 到 路径上所有点权置为
:查询当前以 为根的子树中节点权值的最小值
思路
如果没有换根操作,那么树剖后用线段树维护很容易解决。对于换根操作,无论根是谁,我们都只在一开始令 为根进行一次树剖,之后的换根可以分为几种情况,首先是如果查询的节点正好是当前的根,那么直接就是整棵树的信息,如果不是,那么还分为两种情况,考虑从 到 的路径,如果查询的节点 不在这条路径上,查询仍然为以 为根时的以 为根的子树信息,如果在这条路径上,设 在这条路径上的儿子节点为 ,那么查询的信息就是以 为根时整棵树的信息去掉以 为根节点的子树的信息。对于 ,我们可以直接在树上跳链来寻找。
代码
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mk make_pair
//#define int LL
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf INT_MAX
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 1000010;
int N, M, A[maxn], root;
vector<int>G[maxn];
int fa[maxn], dep[maxn], siz[maxn], hson[maxn], top[maxn], dfn[maxn], rnk[maxn], tot = 0;
void add_edge(int from, int to)
{
G[from].push_back(to);
G[to].push_back(from);
}
void dfs1(int v, int p, int d)
{
fa[v] = p, dep[v] = d, siz[v] = 1;
for (auto& to : G[v])
{
if (to == p)
continue;
dfs1(to, v, d + 1);
siz[v] += siz[to];
if (!hson[v] || siz[to] > siz[hson[v]])
hson[v] = to;
}
}
void dfs2(int v, int p, int t)
{
dfn[v] = ++tot, rnk[tot] = v, top[v] = t;
if (!hson[v])
return;
dfs2(hson[v], v, t);
for (auto& to : G[v])
{
if (to == p || to == hson[v])
continue;
dfs2(to, v, to);
}
}
int find(int v)
{
int tmp = root;
while (top[v] != top[tmp])
{
if (fa[top[tmp]] == v)
return top[tmp];
tmp = fa[top[tmp]];
if (tmp == 0)
return 0;
}
if (dfn[v] > dfn[tmp])
return 0;
return hson[v];
}
struct Node {
int dat, l, r, lazy;
}tr[maxn * 4];
void pushup(int p)
{
tr[p].dat = min(tr[lc].dat, tr[rc].dat);
}
void pushdown(int p)
{
if (tr[p].lazy)
{
tr[lc].dat = tr[lc].lazy = tr[p].lazy;
tr[rc].dat = tr[rc].lazy = tr[p].lazy;
tr[p].lazy = 0;
}
}
void build(int p, int l, int r)
{
tr[p].l = l, tr[p].r = r;
if (l + 1 == r)
{
tr[p].dat = A[rnk[l]];
return;
}
int mid = (l + r) / 2;
build(lc, l, mid), build(rc, mid, r);
pushup(p);
}
void modify(int p, int l, int r, int val)
{
if (tr[p].l >= l && tr[p].r <= r)
{
tr[p].dat = tr[p].lazy = val;
return;
}
pushdown(p);
int mid = (tr[p].l + tr[p].r) / 2;
if (l < mid)
modify(lc, l, r, val);
if (r > mid)
modify(rc, l, r, val);
pushup(p);
}
int query(int p, int l, int r)
{
if (tr[p].l >= l && tr[p].r <= r)
return tr[p].dat;
pushdown(p);
int mid = (tr[p].l + tr[p].r) / 2;
if (r <= mid)
return query(lc, l, r);
else if (l >= mid)
return query(rc, l, r);
else
return min(query(lc, l, mid), query(rc, mid, r));
}
void solve()
{
dfs1(1, 0, 1), dfs2(1, 0, 1);
build(1, 1, N + 1);
int op, id, x, v, y;
while (M--)
{
cin >> op;
if (op == 1)
{
cin >> id;
root = id;
}
else if (op == 2)
{
cin >> x >> y >> v;
while(top[x] != top[y])
{
if (dep[top[x]] > dep[top[y]])
modify(1, dfn[top[x]], dfn[x] + 1, v), x = fa[top[x]];
else
modify(1, dfn[top[y]], dfn[y] + 1, v), y = fa[top[y]];
}
if (dfn[x] > dfn[y])
swap(x, y);
modify(1, dfn[x], dfn[y] + 1, v);
}
else
{
cin >> id;
if (root == id)
cout << query(1, 1, N + 1) << endl;
else
{
int z = find(id);
if (!z)
cout << query(1, dfn[id], dfn[id] + siz[id]) << endl;
else
{
int ans = query(1, 1, dfn[z]);
if (dfn[z] + siz[z] < N + 1)
ans = min(ans, query(1, dfn[z] + siz[z], N + 1));
cout << ans << endl;
}
}
}
}
}
int main()
{
IOS;
cin >> N >> M;
int u, v;
for (int i = 1; i < N; i++)
cin >> u >> v, add_edge(u, v);
for (int i = 1; i <= N; i++)
cin >> A[i];
cin >> root;
solve();
return 0;
}
本文作者:Prgl
本文链接:https://www.cnblogs.com/Prgl/p/16457612.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步