树
时间限制:10s 空间限制:256MB
题目描述
给定一棵大小为 n 的有根点权树,支持以下操作:
• 换根
• 修改点权
• 查询子树最小值
输入格式
第一行两个整数 n, Q ,分别表示树的大小和操作数。
接下来n行,每行两个整数f,v,第i+1行的两个数表示点i的父亲和点i的权。保证f < i。如 果f = 0,那么i为根。输入数据保证只有i = 1时,f = 0。
接下来 m 行,为以下格式中的一种:
• V x y表示把点x的权改为y
• E x 表示把有根树的根改为点 x
• Q x 表示查询点 x 的子树最小值
输出格式
对于每个 Q ,输出子树最小值。
样例输入
3 7
0 1
1 2
1 3
Q 1
V 1 6
Q 1
V 2 5
Q 1
V 3 4
Q 1
样例输出
1
2
3
4
提示
对于 100% 的数据:n, Q ≤ 10^5。
没想到做此题时,犯下了大量的低级错误。。。
首先这类题我们要利用DFS序使用线段树维护,这是本题的预备知识
对于本题而言,换根操作显然是关键点,如何在保留原树的情况下求出换根后对应子树的MIN呢,这里我们可以通过分情况讨论来解决
设rt为新根,x为查询节点
①如果rt == x,那么就相当于查整个树的MIN,我们输出t[1].min即可
②如果rt在x的子树内,相当于我们本来是从1号节点到x再到rt,现在我们要强制变为rt到x,显然变向的这一部分就不能用了,因此我们想要求到rt到x这条路径上离x最近的那个点,即为x的儿子之一·,然后砍掉这一区间即可,求剩下区间的MIN(因为已经“变向”)因此我们结合求lca里“向上跳”的方法来优化这一过程
③如果不满足上述情况,那么我们就不存在上文所说的变向的情况了,直接查x子树MIN
AC Code:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define rg register
#define il inline
#define maxn 200005
#define lid id << 1
#define rid (id << 1) | 1
#define ll long long
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
struct edge{
int to , next;
}e[maxn << 1];
struct tree{
int l , r , min;
}t[maxn << 2];
int head[maxn] , cnt , val[maxn] , f[maxn][22] , tot , dep[maxn] , q[maxn];
int l[maxn] , r[maxn];
void add(int u ,int v){
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void dfs(int u,int fa){
l[u] = ++tot , q[tot] = u;
dep[u] = dep[fa] + 1;
f[u][0] = fa;
for (rg int i = 1 ; i <= 20; ++i)
f[u][i] = f[f[u][i - 1]][i - 1];
for (rg int i = head[u] ; i ; i = e[i].next){
rg int to = e[i].to;
if (to != fa){
dfs(to , u);
}
}
r[u] = tot;
}
void pushup(int id){
t[id].min = min (t[lid].min , t[rid].min);
}
void build(int id,int l,int r){
t[id].l = l , t[id].r = r;
if (l == r) {t[id].min = val[q[l]];return;}
rg int mid = (l + r) >> 1;
build(lid , l ,mid);
build(rid , mid + 1 , r);
pushup(id);
}
void update(int id,int pos,int x){
if (pos == t[id].l && t[id].l == t[id].r) {t[id].min = x;return;}
rg int mid = (t[id].l + t[id].r) >> 1;
if (pos <= mid) update(lid , pos , x);
else update(rid , pos , x);
pushup(id);
}
int query(int id,int l,int r){
if (l > r) return 1000000000;
if (t[id].l == l && t[id].r == r) return t[id].min;
rg int mid = (t[id].l + t[id].r) >> 1;
if (r <= mid) return query(lid , l ,r);
else if (l > mid) return query(rid , l ,r);
else return min(query(lid , l , mid) , query(rid , mid + 1 , r));
}
char id[20];
int main(){
int n = read(), m = read() , fa , x , y;
for (rg int i = 1; i <= n;++i){
fa = read() , val[i] = read();
if (fa) add (fa , i);
}
int rt = 1;
dfs(1 , 0);
build(1 , 1 , n);
for (rg int i = 1 ; i <= m ; ++i){
scanf("%s",id);
if (id[0] == 'E') rt = read();
else if (id[0] == 'V') {
x = read() , y = read();
update(1 , l[x] , y);
}
else{
x = read();
if (x == rt) printf("%d\n",t[1].min);
else if (l[x] <= l[rt] && r[rt] <= r[x]){
rg int depth = dep[rt] - dep[x] - 1 , y = rt;
for (rg int i = 0 ; i <= 20 ;++i) if (depth & (1 << i)) y = f[y][i];
printf("%d\n",min ( query(1 , 1 , l[y] - 1) , query(1 , r[y] + 1 , n) ));
}
else printf("%d\n",query(1 , l[x] , r[x]));
}
}
return 0;
}